<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta name="keywords" content="imlgw,半岛铁盒,blog,Java博客,程序员,个人博客,java開發,程序員,個人博客,Java">
    <meta name="description" content="大悲无泪，大悟无言，大笑无声。">
    <meta name="author" content="Resolmi">
    
    <title>
        
            深入理解Java虚拟机（二） |
        
        Tadow
    </title>
    
<link rel="stylesheet" href="/css/style.css">

    <link rel="shortcut icon" href="https://static.imlgw.top/blog/20210731/BtJz541CcmJU.ico">
    <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/css/font-awesome.min.css">
    <script id="hexo-configurations">
    let KEEP = window.KEEP || {};
    KEEP.hexo_config = {"hostname":"imlgw.top","root":"/","language":"zh-CN","path":"search.json"};
    KEEP.theme_config = {"toc":{"enable":true,"number":true,"expand_all":true,"init_open":true},"style":{"primary_color":"#0066CC","avatar":"https://static.imlgw.top/blog/20210731/3C7hCSRR3lfq.png","favicon":"https://static.imlgw.top/blog/20210731/BtJz541CcmJU.ico","article_img_align":"left","left_side_width":"260px","content_max_width":"920px","hover":{"shadow":false,"scale":true},"first_screen":{"enable":true,"background_img":"/images/image.svg","description":"Keep It Simple & Stupid."},"scroll":{"progress_bar":{"enable":true},"percent":{"enable":true}}},"local_search":{"enable":true,"preload":false},"code_copy":{"enable":true,"style":"default"},"pjax":{"enable":true},"lazyload":{"enable":true},"version":"3.4.3"};
    KEEP.language_ago = {"second":"%s 秒前","minute":"%s 分钟前","hour":"%s 小时前","day":"%s 天前","week":"%s 周前","month":"%s 月前","year":"%s 年前"};
  </script>
<meta name="generator" content="Hexo 5.4.0"><link rel="stylesheet" href="/css/prism.css" type="text/css"></head>


<body>
<div class="progress-bar-container">
    
        <span class="scroll-progress-bar"></span>
    

    
        <span class="pjax-progress-bar"></span>
        <span class="pjax-progress-icon">
            <i class="fas fa-circle-notch fa-spin"></i>
        </span>
    
</div>


<main class="page-container">

    

    <div class="page-main-content">

        <div class="page-main-content-top">
            <header class="header-wrapper">

    <div class="header-content">
        <div class="left">
            
            <a class="logo-title" href="/">
                Tadow
            </a>
        </div>

        <div class="right">
            <div class="pc">
                <ul class="menu-list">
                    
                        <li class="menu-item">
                            <a class=""
                               href="/"
                            >
                                首页
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/archives"
                            >
                                归档
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/categories"
                            >
                                分类
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/sbe"
                            >
                                订阅
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/links"
                            >
                                友链
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/about"
                            >
                                关于
                            </a>
                        </li>
                    
                    
                        <li class="menu-item search search-popup-trigger">
                            <i class="fas fa-search"></i>
                        </li>
                    
                </ul>
            </div>
            <div class="mobile">
                
                    <div class="icon-item search search-popup-trigger"><i class="fas fa-search"></i></div>
                
                <div class="icon-item menu-bar">
                    <div class="menu-bar-middle"></div>
                </div>
            </div>
        </div>
    </div>

    <div class="header-drawer">
        <ul class="drawer-menu-list">
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/">首页</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/archives">归档</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/categories">分类</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/sbe">订阅</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/links">友链</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/about">关于</a>
                </li>
            
        </ul>
    </div>

    <div class="window-mask"></div>

</header>


        </div>

        <div class="page-main-content-middle">

            <div class="main-content">

                
                    <div class="fade-in-down-animation">
    <div class="article-content-container">

        <div class="article-title">
            <span class="title-hover-animation">深入理解Java虚拟机（二）</span>
        </div>

        
            <div class="article-header">
                <div class="avatar">
                    <img src="https://static.imlgw.top/blog/20210731/3C7hCSRR3lfq.png">
                </div>
                <div class="info">
                    <div class="author">
                        <span class="name">Resolmi</span>
                        
                            <span class="author-label">BOSS</span>
                        
                    </div>
                    <div class="meta-info">
                        <div class="article-meta-info">
    <span class="article-date article-meta-item">
        <i class="fas fa-edit"></i>&nbsp;2019-08-17 00:00:00
    </span>
    
        <span class="article-categories article-meta-item">
            <i class="fas fa-folder"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/categories/JVM/">JVM</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    
    
        <span class="article-tags article-meta-item">
            <i class="fas fa-tags"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/tags/JVM/">JVM</a>&nbsp;
                    </li>
                
                    <li>
                        | <a href="/tags/ClassLoader/">ClassLoader</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    

    
    
        <span class="article-wordcount article-meta-item">
            <i class="fas fa-file-word"></i>&nbsp;<span>14.4k 字</span>
        </span>
    
    
        <span class="article-min2read article-meta-item">
            <i class="fas fa-clock"></i>&nbsp;<span>62 分钟</span>
        </span>
    
    
        <span class="article-pv article-meta-item">
            <i class="fas fa-eye"></i>&nbsp;<span id="busuanzi_value_page_pv"></span>
        </span>
    
</div>

                    </div>
                </div>
            </div>
        

        <div class="article-content markdown-body">
            <blockquote>
<p>这一篇主要讲JVM的类加载机制，本来很久之前就写了，但是这几天又重新学习了一遍，纠正了之前很多错误的观点，然后又补充了很多东西</p>
</blockquote>
<h2 id="类加载的过程"><a href="#类加载的过程" class="headerlink" title="类加载的过程"></a>类加载的过程</h2><p><strong>前言：</strong></p>
<p>在Java语言中，类型的加载，连接和初始化过程都是在<strong>运行期间</strong>完成的，这与那些在<strong>编译期间</strong>需要进行链接工作的语言（C/C++）不同，这样毫无疑问会增加类加载的性能开销，但是会为Java提供高度的灵活性，Java天生可以动态扩展的就是依赖于运行时期<strong>动态加载和动态链接</strong>这个特点实现的，比如一个接口，完全可以在运行时期动态的指定其具体的实现类。又或者用户可以通过类加载器让一个本地的引用运行时从其他地方（网络等）加载一个二进制的流作为程序代码的一部分。</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://static.imlgw.top///20190418/em4YNNmXXmho.png?imageslim"
                      alt="mark"
                ></p>
<h3 id="①加载"><a href="#①加载" class="headerlink" title="①加载"></a>①加载</h3><ul>
<li>通过一个类的全限定名来获取定义此类的二进制字节流。</li>
<li>将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。</li>
<li>在内存中生成一个代表这个类的 <code>java.lang.Class</code> 对象</li>
</ul>
<h4 id="类加载的最终产物"><a href="#类加载的最终产物" class="headerlink" title="类加载的最终产物"></a>类加载的最终产物</h4><blockquote>
<p>类加载的最终产物是位于堆中的<code>Class</code>对象</p>
</blockquote>
<p>《深入理解Java虚拟机》里面介绍的JVM内存结构(p39)</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://static.imlgw.top///20190417/yk2bzxILsCpL.png?imageslim"
                      alt="JVM内存结构"
                ></p>
<p><strong>堆（Heap）</strong>：最大的一块区域，线程共享。所有的对象实例以及数组都要在堆上分配。回收器主要管理的对象。</p>
<blockquote>
<p>The Java Virtual Machine has a <em>heap</em> that is <code>shared</code> among <code>all</code> Java Virtual Machine threads. The heap is the run-time data area from which memory for all class instances and arrays is allocated.</p>
<p>The heap is created on virtual machine <code>start-up</code>. Heap storage for objects is reclaimed by an automatic storage management system (known as a <em>garbage collector(GC)</em>); </p>
<p>摘自  <a class="link"   target="_blank" rel="noopener" href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.3" >官方文档<i class="fas fa-external-link-alt"></i></a></p>
</blockquote>
<p><strong>方法区（Method Area）</strong>：又称为<code>非堆</code>，线程共享。存储类信息、常量、静态变量、即时编译器编译后的代码。</p>
<blockquote>
<p>The Java Virtual Machine has a <em>method area</em> that is <code>shared</code> among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the “text” segment in an operating system process. It stores per-class structures such as the <strong>run-time constant pool</strong>, <strong>field</strong> and <strong>method data</strong>, and the code for methods and constructors, including the special methods (<a class="link"   target="_blank" rel="noopener" href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9" >§2.9<i class="fas fa-external-link-alt"></i></a>) used in class and instance initialization and interface initialization.</p>
</blockquote>
<p>**运行时常量池(run-time constant pool)**：</p>
<p>是方法区的一部分，用于存放编译期生成的各种字面量”123”，”LGW” 等字符串常量池，和符号引用。运行时常量池具有动态性，并非只有Class文件中的内容才能进入运行时常量池，运行期间也能将新的常量放入池中。如String.intern()方法。</p>
<p><strong>方法栈（JVM Stack）</strong>：</p>
<p>线程私有。存储局部变量表、操作栈、动态链接、方法出口，<code>对象指针</code>。</p>
<p><strong>本地方法栈（Native Method Stack）</strong>：</p>
<p>线程私有。为虚拟机使用到的Native 方法服务。如Java使用c或者c++编写的接口服务时，代码在此区运行。</p>
<p><strong>程序计数器（Program Counter Register）</strong>：</p>
<p>线程私有，它可以看作是当前线程所执行的字节码的行号指示器。指向下一条要执行的指令。</p>
<h4 id="加载类的方式"><a href="#加载类的方式" class="headerlink" title="加载类的方式"></a>加载类的方式</h4><ul>
<li>本地磁盘 classpath</li>
<li>内存中加载 ，动态代理？RPC?</li>
<li>通过网络加载.class</li>
<li>从zip，jar中加载</li>
<li>数据库中提取.class</li>
<li>动态编译</li>
</ul>
<h3 id="②连接"><a href="#②连接" class="headerlink" title="②连接:"></a>②连接:</h3><h4 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h4><p><strong>文件格式验证</strong></p>
<ul>
<li><p>是否以魔数<code>0xCAFEBABE</code>（咖啡宝贝）开头</p>
</li>
<li><p>主、次版本号是否在当前虚拟机处理范围之内</p>
</li>
<li><p>常量池中的常量中是否有不被支持的常量类型（检查常量tag标志）</p>
</li>
<li><p>…..</p>
</li>
</ul>
<p><strong>元数据验证</strong></p>
<p>第二阶段是对字节码描述的信息进行语义分析，以保证其描述的信息符合 Java 语言规范的要求</p>
<p><strong>字节码验证</strong></p>
<p>主要目的是通过数据流和控制流分析，确定程序语义是合法的、符合逻辑的</p>
<p>由于数据流验证的高复杂性，虚拟机设计团队为了避免过多的时间消耗在字节码验证阶段，在 JDK 1.6 之后的 Javac 编译器和 Java 虚拟机中进行了一项优化，给方法体的 Code 属性的属性表中增加了一项名为” StackMapTable” 的属性, 只需要检查StackMapTable属性中的记录是否合法皆可以了</p>
<p><strong>符号引用验证</strong></p>
<p>符号引用的校验发生在虚拟机将<code>符号引用</code>转化为<code>直接引用</code>的时候，这个转化动作将在连接的第三阶段—-<strong>解析</strong>阶段中发生，通常需要校验以下内容：</p>
<ul>
<li>符号引用中通过字符串描述的全限定名是否能找到对应的类</li>
<li>在指定类中是否存在符合方法的字段描述以及简单名称说描述的方法和字段。</li>
<li>符号引用中的类，字段，方法的访问性是否可以被当前类访问</li>
<li>…….</li>
</ul>
<p>如果所运行的全部代码（包括自己编写的及第三方包中的代码）都已经被反复使用和验证过，那么在实施阶段就可以考虑使用-Xverify:none 参数来关闭大部分的类验证措施，以缩短虚拟机类加载的时间</p>
<h4 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h4><p>为类的<strong>静态变量</strong>分配内存，并初始化为默认值</p>
<table>
<thead>
<tr>
<th>数据类型</th>
<th>默认值</th>
</tr>
</thead>
<tbody><tr>
<td>boolean</td>
<td>false</td>
</tr>
<tr>
<td>char</td>
<td>‘/u0000’</td>
</tr>
<tr>
<td>byte</td>
<td>(byte)0</td>
</tr>
<tr>
<td>short</td>
<td>(short)0</td>
</tr>
<tr>
<td>int</td>
<td>0</td>
</tr>
<tr>
<td>long</td>
<td>0L</td>
</tr>
<tr>
<td>double</td>
<td>0.0d</td>
</tr>
<tr>
<td>float</td>
<td>0.0f</td>
</tr>
<tr>
<td>reference</td>
<td>null</td>
</tr>
</tbody></table>
<p>🎯 当然并不是所有情况下都会初始化为零值，如果字段表的属性中有<code>ConstantValue</code> ，准备阶段就会直接初始化为这个这个<code>ConstantValue</code>属性的值</p>
<h4 id="解析"><a href="#解析" class="headerlink" title="解析"></a>解析</h4><p>解析阶段是虚拟机将<strong>常量池</strong>内的<strong>符号引用</strong>替换为<strong>直接引用</strong>的过程。</p>
<ul>
<li><strong>符号引用：</strong> 符号引用以一组符号来描述所引用的目标，符号可以是任何形式的字面量，只要使用的时候能无歧义的定位到目标中就行。</li>
<li><strong>直接引用：</strong> 直接引用可以是直接指向目标的指针，相对偏移量或者一个能间接定位到目标的句柄。</li>
</ul>
<p>除 <code>invokedynamic</code>(动态语言支持) 指令以外，虚拟机实现可以对第一次解析的结果进行缓存。</p>
<p>解析动作主要针对类或接口、字段解析、类方法解析、接口方法解析、方法类型解析、方法句柄解析和调用点限定符 7 类符号引用进行。</p>
<p>关于这个也可以看看R大的回答 <a class="link"   target="_blank" rel="noopener" href="https://www.zhihu.com/question/30300585/answer/51335493" >JVM符号引用转换直接引用的过程 <i class="fas fa-external-link-alt"></i></a>( 吹爆我R大 😁</p>
<h3 id="③初始化："><a href="#③初始化：" class="headerlink" title="③初始化："></a>③初始化：</h3><p>初始化阶段就是执行<code>类构造器&lt;clinit&gt;</code>方法的过程</p>
<h4 id="类构造器-lt-client-gt"><a href="#类构造器-lt-client-gt" class="headerlink" title="类构造器&lt;client&gt;"></a>类构造器&lt;client&gt;</h4><p>💡<code>&lt;clinit&gt;</code>方法是由编译器自动收集类中的所有<strong>类变量的赋值</strong>动作和<strong>静态语句块</strong>（static块）中的语句合并产生的，编译器收集的顺序是由语句在源文件中<strong>出现的顺序决定的</strong>，静态语句块只能访问到定义在静态语句块之前的变量，定义在它之后的变量，在前面的静态语句块<strong>可以赋值</strong>，但是<strong>不能访问</strong>（比如print）</p>
<p>💡 和<strong>实例构造器</strong><code>&lt;init&gt;</code> 不同，不需要显式的去调用父类的构造器，虚拟机会保证子类的<code>&lt;clinit&gt;</code>方法执行之前，父类的<code>&lt;clinit&gt;</code>方法已经执行完毕，因此虚拟机中第一个被执行的<code>&lt;clinit&gt;</code> 一定是Object类</p>
<p>💡<code> &lt;clinit&gt;</code> 方法对于类或者接口来说并不是必须的，如果类中没有静态语句块也没有静态变量的<strong>赋值</strong>操作，那么编译器可以不为这个类生成<code>&lt;clinit&gt;</code> 方法</p>
<p>💡 对于接口，不能使用static块，但是可以有静态变量的赋值操作。子类接口的<code>&lt;clinit&gt;</code>方法调用并不保证父接口的<code>&lt;clinit&gt;</code>方法被先调用，只有用到父接口的静态变量的时候，父接口<code>&lt;clinit&gt;</code>方法才会被调用。接口的实现类在初始化时也一样不会执行接口的<code>&lt;clinit&gt;</code>方法。</p>
<p>💡 虚拟机会保证一个类的<code>&lt;clinit&gt;</code>方法在多线程环境中被正确地加锁、同步。如果一个线程的<code>&lt;clinit&gt;</code>方法调用时间过长，就可能造成多个线程阻塞。Demo如下</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ClinitThreadTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> Thread(()-&gt;<span class="keyword">new</span> SimpleObj()).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(()-&gt;<span class="keyword">new</span> SimpleObj()).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(()-&gt;<span class="keyword">new</span> SimpleObj()).start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">SimpleObj</span></span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> AtomicBoolean init=<span class="keyword">new</span> AtomicBoolean(<span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">static</span> &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName()+<span class="string">&quot; i will be init&quot;</span>);</span><br><span class="line">            <span class="keyword">while</span> (init.get())&#123;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;i am finished&quot;</span>);</span><br><span class="line">       &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="为类的静态变量赋予正确的初始值"><a href="#为类的静态变量赋予正确的初始值" class="headerlink" title="为类的静态变量赋予正确的初始值"></a>为类的静态变量赋予正确的初始值</h4><p>下面这个Demo很经典，可以帮助我们理解类加载的过程</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SingleTon</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> SingleTon ins=<span class="keyword">new</span> SingleTon();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span>  <span class="keyword">int</span> x=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> y;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">SingleTon</span><span class="params">()</span></span>&#123;</span><br><span class="line">        x++;y++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> SingleTon <span class="title">getIns</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> ins;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        SingleTon singleTon=getIns();</span><br><span class="line">        System.out.println(singleTon.x);</span><br><span class="line">        System.out.println(singleTon.y);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果不熟悉类加载的过程可能一眼就觉得应该是（1，1），其实不然，结合上面的加载过程分析</p>
<p>🔺 首先加载连接，然后执行准备工作，在执行完<strong>准备阶段</strong>工作后三个静态变量都有了<strong>默认的初始值</strong>，然后进入初始化阶段</p>
<p>🔺 <code>&lt;clinit&gt;</code>初始化阶段会按顺序<strong>从上往下</strong>依次赋予正确的初始值，所以<strong>先执行</strong>了<code>new Singleton()</code>给<code>ins</code>赋初始值，会调用它的构造器，然后x，y都++变为1，<strong>再然后</strong>就会给x，y赋予正确的初始值，x初始值为0，而y没有初始值所以就是（0，1）</p>
<h3 id="Class对象在哪里？"><a href="#Class对象在哪里？" class="headerlink" title="Class对象在哪里？"></a>Class对象在哪里？</h3><p>先说结论，Class对象和其他普通的Java对象一样都是存放在堆中的。</p>
<p>存放在方法区的是类的元数据(InstanceKlass，包括类的常量池( constant pool)  ，域(Field)信息  ，方法(Method)信息 ，除了常量外的所有静态(static)变量 等)，<code>java.lang.Class实例</code>并不负责记录真正的类元数据，而只是对VM内部的<code>InstanceKlass</code>对象的一个包装供Java的反射访问用，在《深入理解Java虚拟机》一书里面说的存放在方法区中应该是有问题的。</p>
<p><strong>类(静态)变量存放在哪里</strong></p>
<p>从JDK 1.3到JDK 6的HotSpot VM，静态变量保存在类的元数据（InstanceKlass）的末尾(永久代)。而从JDK 7开始的HotSpot VM，静态变量则是保存在类的Java镜像（java.lang.Class实例）的末尾，也就是堆中</p>
<p>参考 R大 <a class="link"   target="_blank" rel="noopener" href="https://www.zhihu.com/question/50258991" >知乎回答<i class="fas fa-external-link-alt"></i></a> </p>
<h2 id="Java程序对类的使用方式"><a href="#Java程序对类的使用方式" class="headerlink" title="Java程序对类的使用方式"></a>Java程序对类的使用方式</h2><p>所有的java虚拟机实现必须在每个类或接口被java程序<strong>首次主动使用</strong>时才<strong>初始化</strong>它们</p>
<h3 id="主动使用"><a href="#主动使用" class="headerlink" title="主动使用"></a>主动使用</h3><ul>
<li>创建类的实例（new）</li>
<li>对某个类或接口的静态变量进行读写（getstatic，putstatic）</li>
<li>调用类的静态方法（invokestatic）</li>
<li>反射某个类 （Class.forName()也可以设置不初始化类）</li>
<li>初始化子类时会先初始化父类</li>
<li>启动类 java HelloWorld  包含main函数的类</li>
<li>Jdk1.7的动态语言支持</li>
</ul>
<h3 id="被动使用"><a href="#被动使用" class="headerlink" title="被动使用"></a>被动使用</h3><p>除了上面 7 个之外，其它的都是被动使用，<strong>不会初始化类</strong>，下面的Demo有几个很容易出错的的例子</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> classloader_study.misc;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ClassActiveUse</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;main is init&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ClassNotFoundException </span>&#123;</span><br><span class="line">        <span class="comment">//System.out.println(Obj.t);</span></span><br><span class="line">        <span class="comment">//Obj.getObj();</span></span><br><span class="line">        <span class="comment">//Class.forName(&quot;classloader_study.Obj&quot;);</span></span><br><span class="line">        <span class="comment">//System.out.println(ObjChild.age);</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">//父类会被初始化 通过子类调用父类的静态变量，子类不会初始化但是会被加载 </span></span><br><span class="line">        <span class="comment">//1. System.out.println(ObjChild.n);</span></span><br><span class="line">        <span class="comment">//不会初始化 定义应用数组也不会初始化类，但是会加载类</span></span><br><span class="line">        <span class="comment">//2. Obj [] arrays=new Obj[10]; </span></span><br><span class="line">        <span class="comment">//不会初始化 常量会在编译期间放到常量池中不会初始化类也不会加载，子类加载也一样</span></span><br><span class="line">        <span class="comment">//3. System.out.println(Obj.t); </span></span><br><span class="line">        <span class="comment">//Obj会被初始化   final修饰的复杂化类型再编译期间无法计算得到，会初始化类</span></span><br><span class="line">        <span class="comment">//4. System.out.println(Obj.x); </span></span><br><span class="line">        <span class="comment">// 类加载器去加载</span></span><br><span class="line">        <span class="comment">//5. ClassLoader.loadClass();</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Obj</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span>  <span class="keyword">final</span> <span class="keyword">int</span> t=<span class="number">10</span>;  <span class="comment">//编译期间就已经确定了就是10</span></span><br><span class="line">	</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> n=<span class="number">111</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> x=<span class="keyword">new</span> Random().nextInt(<span class="number">100</span>); <span class="comment">//值不是常量，运行期间才会确定</span></span><br><span class="line">    <span class="keyword">static</span> &#123; </span><br><span class="line">        System.out.println(<span class="string">&quot;Obj is init&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">getObj</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;NULL&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ObjChild</span> <span class="keyword">extends</span> <span class="title">Obj</span></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> age=<span class="number">12</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Child is init&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">I</span></span>&#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">static</span> <span class="keyword">int</span> a=<span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>⚡new 一个Obj数组的时候，<strong>会加载</strong>Obj类，<strong>不会初始化</strong>Obj对象，但是会导致另一个类的初始化: <code>Lclassloader_study.misc.Obj</code>  这个类就代表了一个元素类型为Obj的一维数组，数组中应有的属性length和方法clone()都是在这个类实现的，这个类是由JVM在运行期间动态的为我们生成的，这个动作由<code>anewarray</code> 指令触发，而基本类型的数组由 <code>newarray</code> 指令触发。</p>
<p>⚡ final修饰的常量会在<strong>编译期间</strong>就放到调用这个变量的方法的类的常量池中，既不会加载也不会初始化，这一点可以通过反编译<code>ClassActiveUse</code> 看的到，两个类不存在任何关系了，甚至可以在编译完成后<code>将Obj的class文件删掉</code>仍然可以执行，但是后面的另一个final常量很明显在<strong>编译期间无法确定值</strong>，只有在运行期间才能回去到值，所以会加载并初始化类</p>
<p>⚡ 对接口的初始化和对类的初始化有一点不同，接口也有初始化过程，接口和类真正有区别是在上面主动使用的第5点，在<strong>子接口被加载的时候并不要求其父接口全部完成了初始化</strong>，只有在真正使用到父接口的时候才会初始化，这一点可以参考前文的 <a href="#%E2%91%A2%E5%88%9D%E5%A7%8B%E5%8C%96%EF%BC%9A">类构造器部分</a></p>
<blockquote>
<p><code>-XX:+TraceClassLoading</code> 可以用来追踪类的加载信息并且打印出来</p>
</blockquote>
<h3 id="对象的访问定位"><a href="#对象的访问定位" class="headerlink" title="对象的访问定位"></a>对象的访问定位</h3><p><strong>如果直接使用句柄访问</strong>，java堆中将会划分出一块内存来作为句柄池，reference中存储的是对象的句柄地址，而句柄中包含了对象数据与类型数据各自的具体地址信息，如下图所示。</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://static.imlgw.top///20190418/FRA9GB31iiXH.png?imageslim"
                      alt="mark"
                ></p>
<p><strong>如果使用直接指针访问</strong>，那么java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息，而reference中存储的直接就是对象地址，如下图所示，每个对象都有一个自己Class对象的引用(<code>getClass</code>)</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://static.imlgw.top///20190418/1OtoI6wkivdz.png?imageslim"
                      alt="mark"
                ></p>
<p>这两种对象访问方式各有优势，使用句柄来访问的最大好处是reference中存储的是稳定的句柄地址，在对象被移动时只会改变句柄中的实例数据指针，而reference本身不需要修改。</p>
<p>使用直接指针访问方式的最大好处就是速度更快，它节省了一次指针定位的时间开销。HotSpot虚拟机使用的是直接指针访问的方式。句柄来访问的情况也十分常见。</p>
<h2 id="JVM类加载器"><a href="#JVM类加载器" class="headerlink" title="JVM类加载器"></a>JVM类加载器</h2><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><blockquote>
<p>虚拟机设计团队把加载阶段中的 <code>“通过一个类的全限定名来获取描述此类的二进制字节流”</code> 这个动作被放到Java虚拟机外部去实现，以便于让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为 <code>“类加载器”</code></p>
</blockquote>
<p> 类加载器并不需要等到某个类被 “首次使用” 时才加载它，这一点从前面讲解的 <a href="#%E8%A2%AB%E5%8A%A8%E4%BD%BF%E7%94%A8">被动使用的例子</a> 哪里就看得出来</p>
<p>JVM规范允许类加载器在预料某个类将要被使用的时候就预先加载它，如果在预先加载的过程中遇到了class文件缺失或者存在错误，类加载器必须在程序<strong>首次主动使用</strong>该类时才报告错误（LinkageError错误）</p>
<p> 如果这个类一直没有被程序主动使用，那么类加载器就不会报告错误</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://static.imlgw.top///20190419/pKeI6Bx6lbcX.png?imageslim"
                      alt="JVM类加载器"
                ></p>
<p><strong>根（Bootstrap）类加载器</strong></p>
<p>该加载器没有父加载器。它负责加载虚拟机的核心类库，如java.lang.*等。根类加载器从系统属性sun.boot.class.path所指定的目录中加载类库。根类加载器的实现依赖于底层操作系统，属于虚拟机的实现的一部分，它并没有继承 java.lang.ClassLoader类(c/c++实现的)。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">System.out.println(System.getProperty(<span class="string">&quot;sun.boot.class.path&quot;</span>));</span><br><span class="line">D:\java\jre\lib\resources.jar;D:\java\jre\lib\rt.jar;D:\java\jre\lib\sunrsasign.jar;D:\java\jre\lib\jsse.jar;D:\java\jre\lib\jce.jar;D:\java\jre\lib\charsets.jar;D:\java\jre\lib\jfr.jar;D:\java\jre\classes</span><br></pre></td></tr></table></figure>

<p><strong>扩展（Extension）类加载器</strong></p>
<p>它的父加载器为根类加载器。它从java.ext.dirs系统属性所指定的目录中加载类库，或者从JDK的安装目录的jre.lib.ext子目录（扩展目录）下加载类库，如果把用户创建的<strong>JAR</strong>文件放在这个目录下，也会自动由扩展类加载器加载。扩展类加载器是纯Java类，是 java.lang.ClassLoader类的子类。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">System.out.println(System.getProperty(<span class="string">&quot;java.ext.dirs&quot;</span>)); <span class="comment">//java.ext.dirs属性</span></span><br><span class="line">D:\java\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext</span><br></pre></td></tr></table></figure>

<p><strong>系统（System）类加载器</strong></p>
<p>也称为应用类加载器，它的父加载器为扩展类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类，它是用户自定义的类加载器的<code>默认父加载器</code>。系统类加载器是纯Java类，是java.lang.ClassLoader类的子类。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">System.out.println(System.getProperty(<span class="string">&quot;java.class.path&quot;</span>));</span><br><span class="line">D:\java\jre\lib\charsets.jar;D:\java\jre\lib\deploy.jar;</span><br><span class="line">D:\java\jre\lib\ext\access-bridge-<span class="number">64.</span>jar;</span><br><span class="line">D:\java\jre\lib\ext\cldrdata.jar;</span><br><span class="line">D:\java\jre\lib\ext\dnsns.jar;</span><br><span class="line">D:\java\jre\lib\ext\jaccess.jar;</span><br><span class="line">D:\java\jre\lib\ext\jfxrt.jar;</span><br><span class="line">D:\java\jre\lib\ext\localedata.jar;</span><br><span class="line">D:\java\jre\lib\ext\nashorn.jar;</span><br><span class="line">D:\java\jre\lib\ext\sunec.jar;</span><br><span class="line">D:\java\jre\lib\ext\sunjce_provider.jar;</span><br><span class="line">D:\java\jre\lib\ext\sunmscapi.jar;</span><br><span class="line">D:\java\jre\lib\ext\sunpkcs11.jar;</span><br><span class="line">D:\java\jre\lib\ext\zipfs.jar;</span><br><span class="line">D:\java\jre\lib\javaws.jar;</span><br><span class="line">D:\java\jre\lib\jce.jar;</span><br><span class="line">D:\java\jre\lib\jfr.jar;</span><br><span class="line">D:\java\jre\lib\jfxswt.jar;</span><br><span class="line">D:\java\jre\lib\jsse.jar;</span><br><span class="line">D:\java\jre\lib\management-agent.jar;</span><br><span class="line">D:\java\jre\lib\plugin.jar;</span><br><span class="line">D:\java\jre\lib\resources.jar;</span><br><span class="line">D:\java\jre\lib\rt.jar;</span><br><span class="line">C:\WorkSpace\concurrent_package\out\production\concurrent_package;</span><br><span class="line">C:\JetBrains\IntelliJ IDEA <span class="number">2018.1</span><span class="number">.4</span>\lib\idea_rt.jar</span><br></pre></td></tr></table></figure>

<blockquote>
<p>其实所谓的父子加载器并不是继承的父子关系，而是包含的关系，子加载器中包含一个父加载器的引用</p>
</blockquote>
<h3 id="自定义类加载器"><a href="#自定义类加载器" class="headerlink" title="自定义类加载器"></a>自定义类加载器</h3><p><strong>先看下JDK的ClassLoader(1.8)源码</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protected</span> Class&lt;?&gt; loadClass(String name, <span class="keyword">boolean</span> resolve)</span><br><span class="line">       <span class="keyword">throws</span> ClassNotFoundException</span><br><span class="line">   &#123;</span><br><span class="line">       <span class="keyword">synchronized</span> (getClassLoadingLock(name)) &#123;</span><br><span class="line">           <span class="comment">// First, check if the class has already been loaded</span></span><br><span class="line">           Class&lt;?&gt; c = findLoadedClass(name);</span><br><span class="line">           <span class="keyword">if</span> (c == <span class="keyword">null</span>) &#123;</span><br><span class="line">               <span class="keyword">long</span> t0 = System.nanoTime();</span><br><span class="line">               <span class="keyword">try</span> &#123;</span><br><span class="line">                   <span class="keyword">if</span> (parent != <span class="keyword">null</span>) &#123;</span><br><span class="line">                       c = parent.loadClass(name, <span class="keyword">false</span>);</span><br><span class="line">                   &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                       <span class="comment">//没有父加载器就交给根加载器</span></span><br><span class="line">                       c = findBootstrapClassOrNull(name);</span><br><span class="line">                   &#125;</span><br><span class="line">               &#125; <span class="keyword">catch</span> (ClassNotFoundException e) &#123;</span><br><span class="line">                   <span class="comment">// ClassNotFoundException thrown if class not found</span></span><br><span class="line">                   <span class="comment">// from the non-null parent class loader</span></span><br><span class="line">               &#125;</span><br><span class="line"></span><br><span class="line">               <span class="keyword">if</span> (c == <span class="keyword">null</span>) &#123;</span><br><span class="line">                   <span class="comment">// If still not found, then invoke findClass in order</span></span><br><span class="line">                   <span class="comment">// to find the class.</span></span><br><span class="line">                   <span class="keyword">long</span> t1 = System.nanoTime();</span><br><span class="line">                   <span class="comment">//需要子类去实现</span></span><br><span class="line">                   c = findClass(name);</span><br><span class="line"></span><br><span class="line">                   <span class="comment">// this is the defining class loader; record the stats</span></span><br><span class="line">                   sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);</span><br><span class="line">                   sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);</span><br><span class="line">                   sun.misc.PerfCounter.getFindClasses().increment();</span><br><span class="line">               &#125;</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="keyword">if</span> (resolve) &#123;</span><br><span class="line">               resolveClass(c);</span><br><span class="line">           &#125;</span><br><span class="line">           <span class="keyword">return</span> c;</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<p>这是ClassLoader类加载类的方法，可以看到中间有一段 </p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span>(parent!=<span class="keyword">null</span>)&#123;  c = parent.loadClass(name, <span class="keyword">false</span>);&#125;</span><br></pre></td></tr></table></figure>

<p>这就是<strong>双亲委派机制</strong>的实现，委托父类，让父类去加载，父类(根加载器)没有就会再层层下降如果有一个加载成功就会成功返回，除此之外还调用了一个没有实现的<code>findClass</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protected</span> Class&lt;?&gt; findClass(String name) <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> ClassNotFoundException(name);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>也就是说想自定义类加载器的话就得重写<code>findClass</code>方法，实际上这个<code>findClass</code>才是类加载的核心，真正加载Class文件转换为CLass实例的就是<code>findClass</code>方法， <code>loadClass()</code>只是实现加载的逻辑，比如<strong>双亲委派机制</strong></p>
<p><strong>实现一个简易的ClassLoader</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyClassLoader</span> <span class="keyword">extends</span> <span class="title">ClassLoader</span> </span>&#123;</span><br><span class="line">    <span class="comment">//将字节码放到这个路径下</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String DEFAULT_DIR = <span class="string">&quot;D:\\ClassLoaderTest&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String dir = DEFAULT_DIR;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String classLoaderName;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyClassLoader</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyClassLoader</span><span class="params">(String classLoaderName)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.classLoaderName = classLoaderName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MyClassLoader</span><span class="params">(ClassLoader parent, String classLoaderName)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(parent);</span><br><span class="line">        <span class="keyword">this</span>.classLoaderName = classLoaderName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getDir</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> dir;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setDir</span><span class="params">(String dir)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.dir = dir;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getClassLoaderName</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> classLoaderName;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * xx.xx.xx.xx.xx.AAA</span></span><br><span class="line"><span class="comment">     * xx/xx/xx/xx/xx/.AAA.class</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> name</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> ClassNotFoundException</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt; findClass(String name) <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">        String classPath = name.replace(<span class="string">&quot;.&quot;</span>, <span class="string">&quot;/&quot;</span>);</span><br><span class="line">        File classFile = <span class="keyword">new</span> File(dir, classPath + <span class="string">&quot;.class&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (!classFile.exists()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ClassNotFoundException(<span class="string">&quot;The class &quot;</span> + name + <span class="string">&quot; not found under &quot;</span> + dir);</span><br><span class="line">        &#125;</span><br><span class="line">		<span class="comment">//字节码文件的字节流</span></span><br><span class="line">        <span class="keyword">byte</span>[] classBytes = loadClassBytes(classFile);</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">null</span> == classBytes || classBytes.length == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ClassNotFoundException(<span class="string">&quot;load the class &quot;</span> + name + <span class="string">&quot; failed&quot;</span>);</span><br><span class="line">		<span class="comment">//defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.defineClass(name, classBytes, <span class="number">0</span>, classBytes.length);</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="comment">//将文件流转换为字节流</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">byte</span>[] loadClassBytes(File classFile) &#123;</span><br><span class="line">        <span class="keyword">try</span> (ByteArrayOutputStream baos = <span class="keyword">new</span> ByteArrayOutputStream();</span><br><span class="line">             FileInputStream fis = <span class="keyword">new</span> FileInputStream(classFile)) &#123;</span><br><span class="line">            <span class="keyword">byte</span>[] buffer = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">1024</span>];</span><br><span class="line">            <span class="keyword">int</span> len;</span><br><span class="line">            <span class="keyword">while</span> ((len = fis.read(buffer)) != -<span class="number">1</span>) &#123;</span><br><span class="line">                baos.write(buffer, <span class="number">0</span>, len);</span><br><span class="line">            &#125;</span><br><span class="line">            baos.flush();</span><br><span class="line">            <span class="keyword">return</span> baos.toByteArray();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class，前提是Class文件是合法的。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//自定义的需要加载的类</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyObject</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;MyObject static is init&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Hello</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Hello World&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>测试自定义的ClassLoader</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ClassLoaderTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException </span>&#123;</span><br><span class="line">        MyClassLoader loader=<span class="keyword">new</span> MyClassLoader(<span class="string">&quot;Resolmi&quot;</span>);</span><br><span class="line">        <span class="comment">//Class&lt;?&gt; aClass = loader.loadClass(&quot;classloader_study.myClassLoader.MyObject&quot;);</span></span><br><span class="line">        Class&lt;?&gt; aClass = Class.forName(<span class="string">&quot;classloader_study.myClassLoader.MyObject&quot;</span>, <span class="keyword">true</span>, loader);</span><br><span class="line">        System.out.println(aClass);</span><br><span class="line">        System.out.println(aClass.getClassLoader());</span><br><span class="line"></span><br><span class="line">        Object obj=aClass.newInstance();</span><br><span class="line">        Method hello = aClass.getMethod(<span class="string">&quot;Hello&quot;</span>, <span class="keyword">null</span>);</span><br><span class="line">        hello.invoke(obj, <span class="keyword">new</span> Object[]&#123;&#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里直接<code>loadClass</code>或者<code>Class.forName()</code>都可以，通常我们的forName()都是默认用的<strong>AppClassLoader</strong>也就是系统加载器，但是我们也可以把我们的自定义加载器传递进去。</p>
<blockquote>
<p>tip: loadClass 不会初始化类，不属于上面提到的6种主动使用的方式，属于被动使用，Class.forName 第二个参数就是控制是否初始化</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">MyObject <span class="keyword">static</span> is init</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">classloader_study</span>.<span class="title">myClassLoader</span>.<span class="title">MyObject</span></span></span><br><span class="line"><span class="class"><span class="title">classloader_study</span>.<span class="title">myClassLoader</span>.<span class="title">MyClassLoader</span>@74<span class="title">a14482</span></span></span><br><span class="line"><span class="class"><span class="title">Hello</span> <span class="title">World</span></span></span><br></pre></td></tr></table></figure>

<p>如果使用了<code>ide</code>的话，这里很有可能编译器帮你自动编译了，也就是在你的classpath里面已经有字节码文件了，所以就直交给AppClassLoader加载了，所以需要将classpath里面的删掉，将字节码拷贝到你自定义的classLoader指定的目录里面。</p>
<h3 id="双亲委派模式"><a href="#双亲委派模式" class="headerlink" title="双亲委派模式"></a>双亲委派模式</h3><p>即在类加载的时候，系统会判断当前类是否已经被加载，如果被加载，就会直接返回可用的类，否则就会尝试加载，在尝试加载时，会先请求双亲处理，如果双亲请求失败，则会自己加载</p>
<p>这里光看几个内置的ClassLoader可能还不太清晰这里用我们自定义的Loader来试试</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyClassLoaderTest2</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> NoSuchMethodException, IllegalAccessException, InstantiationException, ClassNotFoundException </span>&#123;</span><br><span class="line">        <span class="comment">//loader1的加载目录是D:\\ClassLoaderTest（默认的）</span></span><br><span class="line">        MyClassLoader loader1=<span class="keyword">new</span> MyClassLoader(<span class="string">&quot;Resolmi-father&quot;</span>);</span><br><span class="line">        <span class="comment">//设置loader1为loader2的父加载器</span></span><br><span class="line">        MyClassLoader loader2=<span class="keyword">new</span> MyClassLoader(loader1,<span class="string">&quot;Resolmi2-son&quot;</span>);</span><br><span class="line">        <span class="comment">//这个类存放在 D:\\ClassLoaderTest\\classloader_study\\myClassLoader 下面</span></span><br><span class="line">        Class&lt;?&gt; aClass = loader2.loadClass(<span class="string">&quot;classloader_study.myClassLoader.MyObject&quot;</span>);</span><br><span class="line">        <span class="comment">//设置Loader2的加载目录，这是个空目录，下面什么都没有</span></span><br><span class="line">        loader2.setDir(<span class="string">&quot;D:\\ClassLoaderTest2&quot;</span>);</span><br><span class="line">        System.out.println(aClass);</span><br><span class="line">        System.out.println(aClass.getClassLoader());</span><br><span class="line">        System.out.println(((MyClassLoader)aClass.getClassLoader()).getClassLoaderName());</span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>控制台打印如下</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">classloader_study</span>.<span class="title">myClassLoader</span>.<span class="title">MyObject</span></span></span><br><span class="line"><span class="class"><span class="title">classloader_study</span>.<span class="title">myClassLoader</span>.<span class="title">MyClassLoader</span>@74<span class="title">a14482</span></span></span><br><span class="line"><span class="class"><span class="title">Resolmi</span>-<span class="title">father</span></span></span><br></pre></td></tr></table></figure>

<p>我们用<code>Loader2</code>去加载类，但是这个类的加载目录是空的，然后我们指定<code>Loader2</code> 的父加载器为<code>Loader1</code>而<code>loader1</code> 得加载路径就是要加载得类的路径，可以看到这个这个类最终还是被加载出来了，而且是被 <code>loader1</code>加载出来的，也就是<code>loader2</code>把加载任务委托给了父加载器<code>loader1</code>,然后层层委托再回到<code>loader1</code>，由它加载。</p>
<p>到这里可能会有疑问，为什么要用双亲委派模式？这样走一圈多慢啊，其实这样做主要有两个方面的原因</p>
<p>💡 提高系统安全性，使得 Java 类随着它的类加载器一起具有一种带有优先级的层次关系，从而使得基础类得到统一。对于一些系统核心类，用户自定义的不起作用了，因为都会交给<code>BootStrapLoader</code>去加载，比如自定义了一个java.lang.String的类，然后在加载的时候经过层层委派最后会交给 <code>BootStrapLoader</code>去加载然后返回，所以你自定义的String根本没有加载的机会，这样就避免了用户篡改Java核心的类</p>
<p>💡 避免重复加载，父Loader已经加载过的类，子Loader就不用再加载了，比如<code>Object类</code>这个类在<code>rt.jar</code>下，所以无论是在哪种环境下，最终都会交给<code>BootStrapClassLoader</code>去加载这个类，加载得到的都是同一个<code>Object类</code>，如果不采用双亲委派机制，让各个Loader自己加载那么可能加载出来的就会有很多个Object类（不是同一个Object类，下面会说到）</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Exception in thread <span class="string">&quot;main&quot;</span> java.lang.SecurityException: Prohibited <span class="keyword">package</span> name: java.lang</span><br></pre></td></tr></table></figure>

<h3 id="加密解密类加载器"><a href="#加密解密类加载器" class="headerlink" title="加密解密类加载器"></a>加密解密类加载器</h3><p>本质上和上面的没什么区别，就是多了解密的功能，这里首先用加密工具类加密class</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">EncryptUtil</span> </span>&#123;</span><br><span class="line">	<span class="comment">//相当于密钥</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">byte</span> ENCRYPT_FACTOR = (<span class="keyword">byte</span>) <span class="number">0xff</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">EncryptUtil</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">encrypt</span><span class="params">(String source, String target)</span> <span class="keyword">throws</span> FileNotFoundException </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> (FileInputStream in= <span class="keyword">new</span> FileInputStream(source); FileOutputStream out = <span class="keyword">new</span> FileOutputStream(target)) &#123;</span><br><span class="line">            <span class="keyword">int</span> data;</span><br><span class="line">            <span class="keyword">while</span> ((data=in.read())!=-<span class="number">1</span>)&#123;</span><br><span class="line">                out.write(data^ENCRYPT_FACTOR);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> FileNotFoundException </span>&#123;</span><br><span class="line">        encrypt(<span class="string">&quot;D:\\ClassLoaderTest\\classloader_study\\myClassLoader\\MyObject.class&quot;</span>,<span class="string">&quot;D:\\ClassLoaderTest\\classloader_study\\myClassLoader\\MyObject2.class&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后用加密解密类加载器加载这个类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DecryptClassLoader</span> <span class="keyword">extends</span> <span class="title">ClassLoader</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String DEFAULT_DIR = <span class="string">&quot;D:\\ClassLoaderTest&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String dir = DEFAULT_DIR;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DecryptClassLoader</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DecryptClassLoader</span><span class="params">(ClassLoader parent)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(parent);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> Class&lt;?&gt; findClass(String name) <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">        String classPath = name.replace(<span class="string">&quot;.&quot;</span>, <span class="string">&quot;/&quot;</span>);</span><br><span class="line">        File classFile = <span class="keyword">new</span> File(dir, classPath + <span class="string">&quot;.class&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (!classFile.exists()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ClassNotFoundException(<span class="string">&quot;没找到对应的类文件 ：&quot;</span> + dir);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">byte</span>[] classBytes = loadClassByte(classFile);</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">null</span> == classBytes || classBytes.length == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ClassNotFoundException(<span class="string">&quot;加载失败&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.defineClass(name, classBytes, <span class="number">0</span>, classBytes.length);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">byte</span>[] loadClassByte(File classFile) &#123;</span><br><span class="line">        <span class="keyword">try</span> (FileInputStream in = <span class="keyword">new</span> FileInputStream(classFile); ByteArrayOutputStream baos = <span class="keyword">new</span> ByteArrayOutputStream()) &#123;</span><br><span class="line">            <span class="keyword">int</span> data;</span><br><span class="line">            <span class="keyword">while</span> ((data=in.read())!=-<span class="number">1</span>)&#123;</span><br><span class="line">                <span class="comment">//主要就是这里发生了变化，异或了0xff</span></span><br><span class="line">                baos.write(data^<span class="number">0xff</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> baos.toByteArray();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setDir</span><span class="params">(String dir)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.dir = dir;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当然结果是加载成功啦😋，这里如果用其他的类加载器加载，或者把0xff那里去掉，就会报如下错误</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Exception in thread <span class="string">&quot;main&quot;</span> java.lang.ClassFormatError: Incompatible magic value <span class="number">889275713</span> in <span class="class"><span class="keyword">class</span> <span class="title">file</span> <span class="title">classloader_study</span>/<span class="title">myClassLoader</span>/<span class="title">MyObject</span></span></span><br><span class="line"><span class="class">	<span class="title">at</span> <span class="title">java</span>.<span class="title">lang</span>.<span class="title">ClassLoader</span>.<span class="title">defineClass1</span>(<span class="title">Native</span> <span class="title">Method</span>)</span></span><br><span class="line"><span class="class">	<span class="title">at</span> <span class="title">java</span>.<span class="title">lang</span>.<span class="title">ClassLoader</span>.<span class="title">defineClass</span>(<span class="title">ClassLoader</span>.<span class="title">java</span>:763)</span></span><br><span class="line"><span class="class">	<span class="title">at</span> <span class="title">java</span>.<span class="title">lang</span>.<span class="title">ClassLoader</span>.<span class="title">defineClass</span>(<span class="title">ClassLoader</span>.<span class="title">java</span>:642)</span></span><br><span class="line"><span class="class">	<span class="title">at</span> <span class="title">classloader_study</span>.<span class="title">myClassLoader</span>.<span class="title">MyClassLoader</span>.<span class="title">findClass</span>(<span class="title">MyClassLoader</span>.<span class="title">java</span>:64)</span></span><br><span class="line"><span class="class">	<span class="title">at</span> <span class="title">java</span>.<span class="title">lang</span>.<span class="title">ClassLoader</span>.<span class="title">loadClass</span>(<span class="title">ClassLoader</span>.<span class="title">java</span>:424)</span></span><br><span class="line"><span class="class">	<span class="title">at</span> <span class="title">java</span>.<span class="title">lang</span>.<span class="title">ClassLoader</span>.<span class="title">loadClass</span>(<span class="title">ClassLoader</span>.<span class="title">java</span>:357)</span></span><br><span class="line"><span class="class">	<span class="title">at</span> <span class="title">classloader_study</span>.<span class="title">encryption_study</span>.<span class="title">ClassLoaderTest</span>.<span class="title">main</span>(<span class="title">ClassLoaderTest</span>.<span class="title">java</span>:15)</span></span><br></pre></td></tr></table></figure>

<p>这个异常是链接阶段验证的错误，是上面提到的<code>defineClass()</code>抛出来的，因为你加了密，JVM在加载这个二进制流的时候就无法识别了自然就无法加载。</p>
<h3 id="打破双亲委派机制"><a href="#打破双亲委派机制" class="headerlink" title="打破双亲委派机制"></a>打破双亲委派机制</h3><p><strong>覆盖loadClass()</strong></p>
<p>要打破双亲委派机制主要就是要覆盖<code>loadClass()</code>方法，自己定义加载类的方式。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protected</span> Class&lt;?&gt; loadClass(String name, <span class="keyword">boolean</span> resolve)</span><br><span class="line">        <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">    Class&lt;?&gt; clazz = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//让父加载器加载java核心的包，因为有些类是继承的Java内部的核心类比如Object类</span></span><br><span class="line">    <span class="keyword">if</span> (name.startsWith(<span class="string">&quot;java.&quot;</span>)) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            ClassLoader system = ClassLoader.getSystemClassLoader();</span><br><span class="line">            <span class="comment">//这里仍然是委托上级</span></span><br><span class="line">            clazz = system.loadClass(name);</span><br><span class="line">            <span class="keyword">if</span> (clazz != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (resolve)</span><br><span class="line">                    resolveClass(clazz);</span><br><span class="line">                <span class="keyword">return</span> clazz;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">//自己先找</span></span><br><span class="line">        clazz = findClass(name);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">//找不到就交给父加载器</span></span><br><span class="line">    <span class="keyword">if</span> (clazz == <span class="keyword">null</span> &amp;&amp; getParent() != <span class="keyword">null</span>) &#123;</span><br><span class="line">        getParent().loadClass(name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> clazz;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其实就是自己先找，找不到才会交给父加载器，然后一个需要注意的就是你想加载的这个类可能继承了Java内部核心的类像<code>Object</code>类，然后要加载子类就要先加载它的父类，而你的这个包里面肯定是加载不到这些Java内部的核心类的，所以这些还是得交给上层的加载器去加载。</p>
<p><strong>测试效果</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SimpleClassLoaderTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ClassNotFoundException, IllegalAccessException, InstantiationException </span>&#123;</span><br><span class="line">        SimpleClassLoader simpleClassLoader = <span class="keyword">new</span> SimpleClassLoader();</span><br><span class="line">        Class&lt;?&gt; aClass = simpleClassLoader.loadClass(<span class="string">&quot;classloader_study.break_parent.SimpleObject&quot;</span>);</span><br><span class="line">        System.out.println(aClass.getClassLoader());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>注意这里在<code>classpath</code>里面是有这个类的字节码文件的，按照双亲委托机制应该由AppClassLoader去加载</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://static.imlgw.top///20190420/5Nc0S6OoIa2D.png?imageslim"
                      alt="mark"
                ></p>
<p>但是仍然是由我们的SimpleClassLoader加载的，说明我们成功了打破了双亲委派机制（貌似Tomcat也是这种加载机制，有时间看看Tomcat的类加载器）</p>
<p><strong>面试题</strong></p>
<p>❓ <strong>能不能自己写个类比如<code>java.lang.String</code>去覆盖Java的<code>String</code>？如果不覆盖<code>loadClass()</code>方法使用双亲委托肯定是不行，但是既然上面已经打破了双亲委托那是不是就可以了呢？</strong></p>
<p><code>Talk is cheap，show me the code</code> 试试就知道了</p>
<p>先准备一个String类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> java.lang;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> imlgw.top</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> 2019/4/18 12:01</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">String</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;i am init&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getVal</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span>  <span class="number">250</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>编译好之后放到我们自定义的<code>ClassLoader</code>的目录下，然后将我们<code>loadClass()</code>方法加载核心包的地方注释掉（不然还是会交给父加载器去加载）然后为了表示是我们自定义的<code>ClassLoader</code>加载的我们把classpath里面的字节码文件也删掉。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ClassNotFoundException, IllegalAccessException, InstantiationException </span>&#123;</span><br><span class="line">       SimpleClassLoader simpleClassLoader = <span class="keyword">new</span> SimpleClassLoader();</span><br><span class="line">       Class&lt;?&gt; aClass = simpleClassLoader.loadClass(<span class="string">&quot;java.lang.String&quot;</span>);</span><br><span class="line">       System.out.println(aClass.getClassLoader());</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<p>然后就会看到如下的<code>SecurityException</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">java.lang.SecurityException: Prohibited <span class="keyword">package</span> name: java.lang</span><br><span class="line">	at java.lang.ClassLoader.preDefineClass(ClassLoader.java:<span class="number">662</span>)</span><br><span class="line">	at java.lang.ClassLoader.defineClass(ClassLoader.java:<span class="number">761</span>)</span><br><span class="line">	at java.lang.ClassLoader.defineClass(ClassLoader.java:<span class="number">642</span>)</span><br><span class="line">	at classloader_study.break_parent.SimpleClassLoader.findClass(SimpleClassLoader.java:<span class="number">52</span>)</span><br><span class="line">	at classloader_study.break_parent.SimpleClassLoader.loadClass(SimpleClassLoader.java:<span class="number">77</span>)</span><br><span class="line">	at java.lang.ClassLoader.loadClass(ClassLoader.java:<span class="number">357</span>)</span><br><span class="line">	at classloader_study.break_parent.SimpleClassLoaderTest.main(SimpleClassLoaderTest.java:<span class="number">7</span>)</span><br></pre></td></tr></table></figure>

<p>其实Java为了安全，自定义类取名不能取<code>java.*</code>等核心包名开头，在<code>preDefineClass</code>里面做了检查，所以即使你打破了双亲委托机制你依然不能去覆盖Java的核心类（肯定不行啊😄）。</p>
<p>其实这里后来了解了<code>Unsafe</code>后也尝试用<code>Unsafe</code> 去加载这个类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protected</span> Class&lt;?&gt; findClass(String name) <span class="keyword">throws</span> ClassNotFoundException &#123;</span><br><span class="line">        Unsafe unsafe=UnsafeTest.getUnsafe();</span><br><span class="line">        String classPath = name.replace(<span class="string">&quot;.&quot;</span>, <span class="string">&quot;/&quot;</span>);</span><br><span class="line">        File classFile = <span class="keyword">new</span> File(dir, classPath + <span class="string">&quot;.class&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (!classFile.exists()) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ClassNotFoundException(<span class="string">&quot;The class &quot;</span> + name + <span class="string">&quot; found under &quot;</span> + dir);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">byte</span>[] classBytes = loadClassBytes(classFile);</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">null</span> == classBytes || classBytes.length == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ClassNotFoundException(<span class="string">&quot;load the class &quot;</span> + name + <span class="string">&quot; failed&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> unsafe.defineClass(<span class="keyword">null</span>,classBytes,<span class="number">0</span>,classBytes.length,SimpleClassLoader.<span class="keyword">this</span>,<span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>loadClass</code> 同上，尝试加载你会发现 会提示找不到<code>Object</code> 类，嗯？已经在加载父类了，难不成还真可以？这里其实已经和上面的方法不同了，上面的方法是不会进入到加载父类的环节的，直接在加载前就被检测了包名然后GG了，随后我在<code>loadClass</code> 中让系统加载器去加载Object类，再次尝试加载</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">java.lang.SecurityException: Prohibited <span class="keyword">package</span> name: java.lang</span><br><span class="line">	at sun.misc.Unsafe.defineClass(Native Method)</span><br><span class="line">	at classloader_study.break_parent.SimpleClassLoader.findClass(SimpleClassLoader.java:<span class="number">57</span>)</span><br><span class="line">	at classloader_study.break_parent.SimpleClassLoader.loadClass(SimpleClassLoader.java:<span class="number">83</span>)</span><br><span class="line">	at java.lang.ClassLoader.loadClass(ClassLoader.java:<span class="number">357</span>)</span><br><span class="line">	at classloader_study.break_parent.SimpleClassLoaderTest.main(SimpleClassLoaderTest.java:<span class="number">7</span>)</span><br><span class="line">Exception in thread <span class="string">&quot;main&quot;</span> java.lang.NullPointerException</span><br><span class="line">	at classloader_study.break_parent.SimpleClassLoaderTest.main(SimpleClassLoaderTest.java:<span class="number">8</span>)</span><br></pre></td></tr></table></figure>

<p>还是熟悉的味道😂 <code>defineClass</code>虚拟机本地方法抛了异常</p>
<h3 id="类加载器细节深入"><a href="#类加载器细节深入" class="headerlink" title="类加载器细节深入"></a>类加载器细节深入</h3><p><strong>定义加载器&amp;初始加载器</strong></p>
<p>⚡真正完成类的加载工作的类加载器和启动这个加载过程的类加载器，有可能不是同一个。真正<code>完成类的加载</code>工作是通过调用 <code>defineClass(findClass)</code>来实现的；而<code>启动</code>类的加载过程是通过调用 <code>loadClass</code>来实现的。前者称为一个类的定义加载器（defining loader），后者称为初始加载器（initiating loader）。在 Java 虚拟机判断两个类是否相同的时候，使用的是类的定义加载器。也就是说，哪个类加载器启动类的加载过程并不重要，重要的是最终定义这个类的加载器。两种类加载器的关联之处在于：<strong>一个类的定义加载器是它引用的其它类的初始加载器</strong>。如类 <code>com.example.Outer</code>引用了类 <code>com.example.Inner</code>，则由类 <code>com.example.Outer</code>的<strong>定义加载器</strong>负责启动类 <code>com.example.Inner</code>的加载过程。</p>
<p><strong>命名空间&amp;运行时包</strong></p>
<blockquote>
<p>每个类都有自己的命名空间，命名空间由<code>该加载器及所有父加载器所加载的类</code>组成，<code>子加载器的命名空间包含所有父加载器的命名空间</code>，因此子加载器可以加载的类可以看就按父加载器加载的类，例如系统类加载器可以看见根加载器加载的类。由父加载器加载的类看不见子加载器加载的类，如果两个类之间没有直接或者间接的父子关系，那么他们各自加载的类相互不可见</p>
</blockquote>
<p>⚡数组类的Class不是由类加载器加载的，是由JVM在运行期间动态生成的，但是通过<code>getClassLoader</code>返回的类加载器和数组的元素的类加载器是一样的，原生的类型比如<code>int</code> 之类的没有类加载器返回的是null</p>
<p>⚡每个类加载器都有其自己的命名空间，命名空间由该加载器和其所有父类加载器所加载的类组成，同一份字节码两个不同的类加载器加载出来的不是同一个类。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyClassLoaderTest2</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> NoSuchMethodException, IllegalAccessException, InstantiationException, ClassNotFoundException </span>&#123;</span><br><span class="line">        <span class="comment">//注意这里的ClassLoader要么利用前面的打破了双亲委托机制的，要么把classpath里面字节码的删掉。</span></span><br><span class="line">        MyClassLoader loader1=<span class="keyword">new</span> MyClassLoader(<span class="string">&quot;Resolmi1&quot;</span>);</span><br><span class="line">        MyClassLoader loader2=<span class="keyword">new</span> MyClassLoader(<span class="string">&quot;Resolmi2&quot;</span>);</span><br><span class="line">        Class&lt;?&gt; aClass2 = loader2.loadClass(<span class="string">&quot;classloader_study.myClassLoader.MyObject&quot;</span>);</span><br><span class="line">        Class&lt;?&gt; aClass1 = loader1.loadClass(<span class="string">&quot;classloader_study.myClassLoader.MyObject&quot;</span>);</span><br><span class="line">        <span class="comment">//两个不同的加载器（没有父子关系）加载同一个类加载出来的不是同一个</span></span><br><span class="line">        System.out.println(aClass1.hashCode()); <span class="comment">//2133927002</span></span><br><span class="line">        System.out.println(aClass2.hashCode()); <span class="comment">//1836019240</span></span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>⚡<strong>父类加载器</strong>无法访问<strong>子类加载器</strong>加载的类，而<strong>子加载器</strong>可以访问到<strong>父加载器</strong>所加载的类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ClassLoaderTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException </span>&#123;</span><br><span class="line">        <span class="comment">//使用上面自定义的ClassLoader</span></span><br><span class="line">        MyClassLoader loader=<span class="keyword">new</span> MyClassLoader(<span class="string">&quot;Resolmi&quot;</span>);</span><br><span class="line">        Class&lt;?&gt; aClass = loader.loadClass(<span class="string">&quot;classloader_study.myClassLoader.Parent&quot;</span>);</span><br><span class="line">        <span class="comment">//创建实例</span></span><br><span class="line">        aClass.newInstance();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>Parent类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Parent</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Parent is load by&quot;</span> + <span class="keyword">this</span>.getClass().getClassLoader());</span><br><span class="line">        Hello();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Hello</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//父加载器访问子加载器加载的类</span></span><br><span class="line">        System.out.println(<span class="string">&quot;Parent can see the &quot;</span> + Sub.class);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>Sub类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Sub</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Sub</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Sub is load by&quot;</span>+<span class="keyword">this</span>.getClass().getClassLoader());</span><br><span class="line">        <span class="keyword">new</span> Parent(); <span class="comment">//构造Parent类</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>做完了这些工作之后，编译代码，然后将classpath中的<code>Parent.class</code>拷贝到自定义的ClassLoader路径下面，然后删掉classpath中的<code>Parent.class</code> ，然后运行</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Sub is load byclassloader_study.myClassLoader.MyClassLoader@74a14482</span><br><span class="line">Parent is load bysun.misc.Launcher$AppClassLoader@18b4aac2</span><br><span class="line">Exception in thread <span class="string">&quot;main&quot;</span> java.lang.NoClassDefFoundError: classloader_study/myClassLoader/Sub</span><br><span class="line">	at classloader_study.myClassLoader.Parent.Hello(Parent.java:<span class="number">15</span>)</span><br><span class="line">	at classloader_study.myClassLoader.Parent.&lt;init&gt;(Parent.java:<span class="number">10</span>)</span><br><span class="line">	at classloader_study.myClassLoader.Sub.&lt;init&gt;(Sub.java:<span class="number">10</span>)</span><br><span class="line">	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)</span><br><span class="line">	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:<span class="number">62</span>)</span><br><span class="line">	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:<span class="number">45</span>)</span><br><span class="line">	at java.lang.reflect.Constructor.newInstance(Constructor.java:<span class="number">423</span>)</span><br><span class="line">	at java.lang.Class.newInstance(Class.java:<span class="number">442</span>)</span><br><span class="line">	at classloader_study.myClassLoader.ClassLoaderTest.main(ClassLoaderTest.java:<span class="number">14</span>)</span><br><span class="line">Caused by: java.lang.ClassNotFoundException: classloader_study.myClassLoader.Sub</span><br><span class="line">	at java.net.URLClassLoader.findClass(URLClassLoader.java:<span class="number">381</span>)</span><br><span class="line">	at java.lang.ClassLoader.loadClass(ClassLoader.java:<span class="number">424</span>)</span><br><span class="line">	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:<span class="number">349</span>)</span><br><span class="line">	at java.lang.ClassLoader.loadClass(ClassLoader.java:<span class="number">357</span>)</span><br><span class="line">	... <span class="number">9</span> more</span><br></pre></td></tr></table></figure>

<p>可以看到，我们的<code>Sub</code> 是由我们自定义的<code>MyClassLoader</code> 加载的，因为我们删掉了Classpath中的字节码，而在自定义的路径中有所以最后还是由我们的 <code>MyClassLoader</code> 加载了，所以我们的<code>Parent</code>类首先也会由我们的<code>自定义类加载器MyClassLoader</code>去作为<strong>初始加载器</strong>加载，由于我们的Classpath中还有字节码文件，所以在双亲委派机制下<code>Sub类</code> 最终由<code>AppClassLoader</code>加载 ，两个类由不同的类加载器加载，然后我们在<code>Parent</code> 类中试图访问<code>Sub</code> 类，结果抛出了<code>ClassNotFoundException</code>  和<code>NoClassDefFoundError</code> 异常</p>
<p>❓ <strong>那我们反过来在<code>Sub</code>类中访问 <code>Parent</code> 类会发生什么，改造一下Parent和Sub</strong></p>
<p><strong>Parent类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Parent</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Parent</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Parent is load by&quot;</span> + <span class="keyword">this</span>.getClass().getClassLoader());</span><br><span class="line">        <span class="comment">//Hello();</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Hello</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">//父加载器访问子加载器加载的类</span></span><br><span class="line">        System.out.println(<span class="string">&quot;Parent can see the &quot;</span> + Sub.class);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>Sub类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Sub</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Sub</span><span class="params">()</span></span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;Sub is load by&quot;</span>+<span class="keyword">this</span>.getClass().getClassLoader());</span><br><span class="line">        <span class="keyword">new</span> Parent(); <span class="comment">//构造Sub类</span></span><br><span class="line">        <span class="comment">//访问父加载器加载的类</span></span><br><span class="line">        System.out.println(<span class="string">&quot;Sub can see &quot;</span>+Parent.class);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>和上面一样删掉Classpath中Sub类的class文件没然后运行</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">findclass is invoke MyClassLoader is loadclassloader_study.myClassLoader.Sub</span><br><span class="line">Sub is load byclassloader_study.myClassLoader.MyClassLoader@74a14482</span><br><span class="line">Parent is load bysun.misc.Launcher$AppClassLoader@18b4aac2</span><br><span class="line">Sub can see <span class="class"><span class="keyword">class</span> <span class="title">classloader_study</span>.<span class="title">myClassLoader</span>.<span class="title">Parent</span></span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class"><span class="title">Process</span> <span class="title">finished</span> <span class="title">with</span> <span class="title">exit</span> <span class="title">code</span> 0</span></span><br></pre></td></tr></table></figure>

<p>没有任何问题，由此就可以证明我们上面的结论是正确的。</p>
<p>❓ <strong>面试题：如何让一个类的static代码块执行两次</strong></p>
<p>用不同的类加载器去加载这个类，至于为什么应该不用我多说了吧。</p>
<p><strong>类的卸载和ClassLoader的卸载</strong></p>
<blockquote>
<p> 由Java虚拟机自带的类加载器所加载的类，在虚拟机的生命周期中<strong>始终不会被卸载</strong>，Java虚拟机本身会引用这些类加载器，而这些类加载器则会始终引用他们所加载的类的Class对象，因此这些Class对象始终是可达的。</p>
</blockquote>
<p>⚡JVM中的Class只有满足以下三个条件，才能被GC回收，也就是该Class被卸载（unload）</p>
<ul>
<li>该类所有的实例都已经被GC。</li>
<li>加载该类的ClassLoader实例已经被GC。(Class对象里面有对ClassLoader的引用)</li>
<li>该类的java.lang.Class对象没有在任何地方被引用。</li>
</ul>
<p>GC的时机我们是不可控的，那么同样的我们对于Class的卸载也是不可控的，使用<code>-XX:+TraceClassUnloading</code> 或者jvisualvm可以看到类的卸载</p>
<h2 id="线程上下文加载器-TCCL"><a href="#线程上下文加载器-TCCL" class="headerlink" title="线程上下文加载器(TCCL)"></a>线程上下文加载器(<strong>TCCL</strong>)</h2><p>​    在说TCCL之前不得不说一下另一个话题，SPI（Service Provider Interface，SPI）服务提供接口，由第三方为这些接口提供实现。常见的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。这些 SPI 的接口由 <strong>Java 核心库</strong>来提供，而这些 SPI 的实现代码则是作为 Java 应用所依赖的 jar 包被包含进<strong>类路径</strong>（classpath）里。SPI接口中的代码经常需要加载具体的实现类。</p>
<blockquote>
<p>为什么要使用SPI?  SPI是JDK内置的一种<strong>服务提供发现机制</strong>。这样做的好处主要是为了解耦，实现动态替换，减少硬编码（比如jdk1.6之前的Class.forName(“XXXX”) ）面向接口编程，在很多开源框架中都有体现，比如Dubbo，Spring等</p>
</blockquote>
<p>那么问题来了，<code>SPI的接口</code>是Java核心库的一部分位于<code>rt.jar</code>中，是由<strong>根加载器</strong>(Bootstrap Classloader)来加载的，而<code>SPI的实现类</code>是一般是第三方的提供的，位于<code>classpath</code>目录中，而<strong>根加载器</strong>很明显是无法直接加载到这个目录下的SPI 的实现类的 (双亲委派)，那<code>SPI</code>是如何自动加载到实现类的呢？</p>
<p>为了解决这个问题，虚拟机提供了<strong>线程上下文加载器（TCCL）</strong>配合<code>ServiceLoader</code>来帮助上层加载器加载类，<code>TCCL</code>破坏了“双亲委派模型”，可以在执行过程中切换为<code>TCCL</code> 来加载第三方的SPI实现类，抛弃双亲委派机制，使程序可以逆向使用类加载器。<strong>TCCL</strong>默认是系统类加载器，也可以通过<code>setContextClassLoader</code>去设置</p>
<h3 id="JDBC源码案例分析"><a href="#JDBC源码案例分析" class="headerlink" title="JDBC源码案例分析"></a>JDBC源码案例分析</h3><p>翻到了最开始学JDBC的时候写的代码😄</p>
<p><img  
                     lazyload
                     src="/images/loading.svg"
                     data-src="http://static.imlgw.top///20190420/K4uQf6h5czDb.png?imageslim"
                      alt="JDBC"
                ></p>
<p><strong>贾琏欲执事</strong>，可以看到第一步还是加载并且初始化驱动，前面已经提到这一步其实没有必要了，jdk1.6之后因为ServiceLoader，SPI机制的出现，就不用再显示的加载驱动，但是正如上面所说<code>Driver</code>只是个接口存放于<code>rt.jar</code> 中，由根加载器所加载，那SPI是怎么自动的加载到<code>mysql</code>的<code>Driver</code>实例的呢？😕</p>
<p>我们一步步的来看，首先<code>DriverManager.getConnection()</code>这里，<code>getConnection</code>是个静态方法，调用它就会先执行<code>DriverManager</code>类的静态代码块，而静态代码块里面主要执行的就是<code>loadInitialDrivers()</code></p>
<h3 id="loadInitialDrivers-源码"><a href="#loadInitialDrivers-源码" class="headerlink" title="loadInitialDrivers()源码"></a>loadInitialDrivers()源码</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">loadInitialDrivers</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    String drivers;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        drivers = AccessController.doPrivileged(<span class="keyword">new</span> PrivilegedAction&lt;String&gt;() &#123;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> String <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">return</span> System.getProperty(<span class="string">&quot;jdbc.drivers&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">        drivers = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// If the driver is packaged as a Service Provider, load it.</span></span><br><span class="line">    <span class="comment">// Get all the drivers through the classloader</span></span><br><span class="line">    <span class="comment">// exposed as a java.sql.Driver.class service.</span></span><br><span class="line">    <span class="comment">// ServiceLoader.load() replaces the sun.misc.Providers()</span></span><br><span class="line">	<span class="comment">// 如果驱动正确打包为jar就会用ServiceLoader去加载它</span></span><br><span class="line">    AccessController.doPrivileged(<span class="keyword">new</span> PrivilegedAction&lt;Void&gt;() &#123;</span><br><span class="line">        <span class="function"><span class="keyword">public</span> Void <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="comment">/****************************************************/</span></span><br><span class="line">			<span class="comment">/*ServiceLoad工具类，注意这个ServiceLoad的加载器，默认就是TCCL*/</span></span><br><span class="line">            ServiceLoader&lt;Driver&gt; loadedDrivers = ServiceLoader.load(Driver.class);</span><br><span class="line">            <span class="comment">/****************************************************/</span></span><br><span class="line">            <span class="comment">/*在这里会获取到一个Drivers的迭代器，但是其实还没有开始加载类*/</span></span><br><span class="line">            Iterator&lt;Driver&gt; driversIterator = loadedDrivers.iterator();</span><br><span class="line"></span><br><span class="line">            <span class="comment">/* Load these drivers, so that they can be instantiated.</span></span><br><span class="line"><span class="comment">             * It may be the case that the driver class may not be there</span></span><br><span class="line"><span class="comment">             * i.e. there may be a packaged driver with the service class</span></span><br><span class="line"><span class="comment">             * as implementation of java.sql.Driver but the actual class</span></span><br><span class="line"><span class="comment">             * may be missing. In that case a java.util.ServiceConfigurationError</span></span><br><span class="line"><span class="comment">             * will be thrown at runtime by the VM trying to locate</span></span><br><span class="line"><span class="comment">             * and load the service.</span></span><br><span class="line"><span class="comment">             *</span></span><br><span class="line"><span class="comment">             * Adding a try catch block to catch those runtime errors</span></span><br><span class="line"><span class="comment">             * if driver not available in classpath but it&#x27;s</span></span><br><span class="line"><span class="comment">             * packaged as service and that service is there in classpath.</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            <span class="keyword">try</span>&#123;</span><br><span class="line">                <span class="keyword">while</span>(driversIterator.hasNext()) &#123;</span><br><span class="line">                    <span class="comment">//迭代的过程中通过next反射加载并初始化这个驱动字节码</span></span><br><span class="line">                    <span class="comment">//没有接收返回的数据库驱动实例</span></span><br><span class="line">                    driversIterator.next();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span>(Throwable t) &#123;</span><br><span class="line">            <span class="comment">// Do nothing</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    println(<span class="string">&quot;DriverManager.initialize: jdbc.drivers = &quot;</span> + drivers);</span><br><span class="line">	<span class="comment">//加载Jdk中的驱动实例（虽然我并不知道是什么）总之我们第三方的驱动已经加载好了</span></span><br><span class="line">    <span class="keyword">if</span> (drivers == <span class="keyword">null</span> || drivers.equals(<span class="string">&quot;&quot;</span>)) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    String[] driversList = drivers.split(<span class="string">&quot;:&quot;</span>);</span><br><span class="line">    println(<span class="string">&quot;number of Drivers:&quot;</span> + driversList.length);</span><br><span class="line">    <span class="keyword">for</span> (String aDriver : driversList) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            println(<span class="string">&quot;DriverManager.Initialize: loading &quot;</span> + aDriver);</span><br><span class="line">            Class.forName(aDriver, <span class="keyword">true</span>,</span><br><span class="line">                    ClassLoader.getSystemClassLoader());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">            println(<span class="string">&quot;DriverManager.Initialize: load failed: &quot;</span> + ex);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到中间有一行很关键的代码</p>
<p> <code>ServiceLoader&lt;Driver&gt; loadedDrivers = ServiceLoader.load(Driver.class);</code></p>
<p>这就是我上面所提到的 <code>ServiceLoader</code> </p>
<blockquote>
<p>对于SPI机制，JDK提供了<code>java.util.ServiceLoader</code>工具类，在使用某个服务接口时，它可以帮助我们查找该服务接口的实现类，加载和初始化，前提条件是基于它的约定：当服务的提供者提供了服务接口的一种实现之后，在<code>jar</code>包的<code>META-INF/services/</code>目录里同时创建一个以<code>服务接口命名的文件</code>。该文件里就是实现该服务接口的具体实现类（去解压看看那些jar包就可以看见这些信息o(<em>￣▽￣</em>)ブ）</p>
</blockquote>
<h3 id="ServiceLoader类源码"><a href="#ServiceLoader类源码" class="headerlink" title="ServiceLoader类源码"></a>ServiceLoader类源码</h3><p>为了节约篇幅删掉了一些注释，发现其实整个类也没多少行大概2，3百行的样子，需要注意这个类并不是线程安全的，所以使用的时候需要注意</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ServiceLoader</span>&lt;<span class="title">S</span>&gt; <span class="keyword">implements</span> <span class="title">Iterable</span>&lt;<span class="title">S</span>&gt; </span>&#123;</span><br><span class="line">	<span class="comment">//目录前缀就是从这里来的</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String PREFIX = <span class="string">&quot;META-INF/services/&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//实现类Service</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Class&lt;S&gt; service;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// The class loader used to locate, load, and instantiate providers</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ClassLoader loader;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// The access control context taken when the ServiceLoader is created</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> AccessControlContext acc;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Cached providers, in instantiation order</span></span><br><span class="line">    <span class="comment">// 按照实例的顺序，来缓存服务提供者避免重复的加载，具体可以看下面的iterator方法</span></span><br><span class="line">    <span class="keyword">private</span> LinkedHashMap&lt;String,S&gt; providers = <span class="keyword">new</span> LinkedHashMap&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// The current lazy-lookup iterator</span></span><br><span class="line">    <span class="keyword">private</span> LazyIterator lookupIterator;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">reload</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        providers.clear();</span><br><span class="line">        <span class="comment">//初始化懒加载迭代器</span></span><br><span class="line">        lookupIterator = <span class="keyword">new</span> LazyIterator(service, loader);</span><br><span class="line">    &#125;</span><br><span class="line">	</span><br><span class="line">    <span class="comment">//构造器</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">ServiceLoader</span><span class="params">(Class&lt;S&gt; svc, ClassLoader cl)</span> </span>&#123;</span><br><span class="line">        service = Objects.requireNonNull(svc, <span class="string">&quot;Service interface cannot be null&quot;</span>);</span><br><span class="line">        loader = (cl == <span class="keyword">null</span>) ? ClassLoader.getSystemClassLoader() : cl;</span><br><span class="line">        acc = (System.getSecurityManager() != <span class="keyword">null</span>) ? AccessController.getContext() : <span class="keyword">null</span>;</span><br><span class="line">        reload();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">fail</span><span class="params">(Class&lt;?&gt; service, String msg, Throwable cause)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> ServiceConfigurationError</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> ServiceConfigurationError(service.getName() + <span class="string">&quot;: &quot;</span> + msg,</span><br><span class="line">                                            cause);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">fail</span><span class="params">(Class&lt;?&gt; service, String msg)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> ServiceConfigurationError</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> ServiceConfigurationError(service.getName() + <span class="string">&quot;: &quot;</span> + msg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">fail</span><span class="params">(Class&lt;?&gt; service, URL u, <span class="keyword">int</span> line, String msg)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> ServiceConfigurationError</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        fail(service, u + <span class="string">&quot;:&quot;</span> + line + <span class="string">&quot;: &quot;</span> + msg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Parse a single line from the given configuration file, adding the name</span></span><br><span class="line">    <span class="comment">// on the line to the names list.</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">parseLine</span><span class="params">(Class&lt;?&gt; service, URL u, BufferedReader r, <span class="keyword">int</span> lc,</span></span></span><br><span class="line"><span class="params"><span class="function">                          List&lt;String&gt; names)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> IOException, ServiceConfigurationError</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        String ln = r.readLine();</span><br><span class="line">        <span class="keyword">if</span> (ln == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">int</span> ci = ln.indexOf(<span class="string">&#x27;#&#x27;</span>);</span><br><span class="line">        <span class="keyword">if</span> (ci &gt;= <span class="number">0</span>) ln = ln.substring(<span class="number">0</span>, ci);</span><br><span class="line">        ln = ln.trim();</span><br><span class="line">        <span class="keyword">int</span> n = ln.length();</span><br><span class="line">        <span class="keyword">if</span> (n != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> ((ln.indexOf(<span class="string">&#x27; &#x27;</span>) &gt;= <span class="number">0</span>) || (ln.indexOf(<span class="string">&#x27;\t&#x27;</span>) &gt;= <span class="number">0</span>))</span><br><span class="line">                fail(service, u, lc, <span class="string">&quot;Illegal configuration-file syntax&quot;</span>);</span><br><span class="line">            <span class="keyword">int</span> cp = ln.codePointAt(<span class="number">0</span>);</span><br><span class="line">            <span class="keyword">if</span> (!Character.isJavaIdentifierStart(cp))</span><br><span class="line">                fail(service, u, lc, <span class="string">&quot;Illegal provider-class name: &quot;</span> + ln);</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = Character.charCount(cp); i &lt; n; i += Character.charCount(cp)) &#123;</span><br><span class="line">                cp = ln.codePointAt(i);</span><br><span class="line">                <span class="keyword">if</span> (!Character.isJavaIdentifierPart(cp) &amp;&amp; (cp != <span class="string">&#x27;.&#x27;</span>))</span><br><span class="line">                    fail(service, u, lc, <span class="string">&quot;Illegal provider-class name: &quot;</span> + ln);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!providers.containsKey(ln) &amp;&amp; !names.contains(ln))</span><br><span class="line">                names.add(ln);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> lc + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> Iterator&lt;String&gt; <span class="title">parse</span><span class="params">(Class&lt;?&gt; service, URL u)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> ServiceConfigurationError</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        InputStream in = <span class="keyword">null</span>;</span><br><span class="line">        BufferedReader r = <span class="keyword">null</span>;</span><br><span class="line">        ArrayList&lt;String&gt; names = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            in = u.openStream();</span><br><span class="line">            r = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(in, <span class="string">&quot;utf-8&quot;</span>));</span><br><span class="line">            <span class="keyword">int</span> lc = <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">while</span> ((lc = parseLine(service, u, r, lc, names)) &gt;= <span class="number">0</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException x) &#123;</span><br><span class="line">            fail(service, <span class="string">&quot;Error reading configuration file&quot;</span>, x);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (r != <span class="keyword">null</span>) r.close();</span><br><span class="line">                <span class="keyword">if</span> (in != <span class="keyword">null</span>) in.close();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException y) &#123;</span><br><span class="line">                fail(service, <span class="string">&quot;Error closing configuration file&quot;</span>, y);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> names.iterator();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Private inner class implementing fully-lazy provider lookup</span></span><br><span class="line">    <span class="comment">// 看名字就知道了，懒迭代器，在迭代的时候才真正的加载</span></span><br><span class="line">    <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">LazyIterator</span></span></span><br><span class="line"><span class="class">        <span class="keyword">implements</span> <span class="title">Iterator</span>&lt;<span class="title">S</span>&gt;</span></span><br><span class="line"><span class="class">    </span>&#123;</span><br><span class="line"></span><br><span class="line">        Class&lt;S&gt; service;</span><br><span class="line">        ClassLoader loader;</span><br><span class="line">        Enumeration&lt;URL&gt; configs = <span class="keyword">null</span>;</span><br><span class="line">        Iterator&lt;String&gt; pending = <span class="keyword">null</span>;</span><br><span class="line">        String nextName = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="title">LazyIterator</span><span class="params">(Class&lt;S&gt; service, ClassLoader loader)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.service = service;</span><br><span class="line">            <span class="keyword">this</span>.loader = loader;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">hasNextService</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (nextName != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (configs == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    String fullName = PREFIX + service.getName();</span><br><span class="line">                    <span class="keyword">if</span> (loader == <span class="keyword">null</span>)</span><br><span class="line">                        configs = ClassLoader.getSystemResources(fullName);</span><br><span class="line">                    <span class="keyword">else</span></span><br><span class="line">                        configs = loader.getResources(fullName);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IOException x) &#123;</span><br><span class="line">                    fail(service, <span class="string">&quot;Error locating configuration files&quot;</span>, x);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">while</span> ((pending == <span class="keyword">null</span>) || !pending.hasNext()) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!configs.hasMoreElements()) &#123;</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                pending = parse(service, configs.nextElement());</span><br><span class="line">            &#125;</span><br><span class="line">            nextName = pending.next();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">		<span class="comment">//迭代器的next</span></span><br><span class="line">        <span class="function"><span class="keyword">private</span> S <span class="title">nextService</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (!hasNextService())</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException();</span><br><span class="line">            String cn = nextName;</span><br><span class="line">            nextName = <span class="keyword">null</span>;</span><br><span class="line">            Class&lt;?&gt; c = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//利用TCCL加载实现类，但是不初始化</span></span><br><span class="line">                c = Class.forName(cn, <span class="keyword">false</span>, loader);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (ClassNotFoundException x) &#123;</span><br><span class="line">                fail(service,</span><br><span class="line">                     <span class="string">&quot;Provider &quot;</span> + cn + <span class="string">&quot; not found&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!service.isAssignableFrom(c)) &#123;</span><br><span class="line">                fail(service,</span><br><span class="line">                     <span class="string">&quot;Provider &quot;</span> + cn  + <span class="string">&quot; not a subtype&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//newInstance()初始化了对应的实现类</span></span><br><span class="line">                S p = service.cast(c.newInstance());</span><br><span class="line">                <span class="comment">//放到providers中</span></span><br><span class="line">                providers.put(cn, p);</span><br><span class="line">                <span class="keyword">return</span> p;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Throwable x) &#123;</span><br><span class="line">                fail(service,</span><br><span class="line">                     <span class="string">&quot;Provider &quot;</span> + cn + <span class="string">&quot; could not be instantiated&quot;</span>,</span><br><span class="line">                     x);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Error();          <span class="comment">// This cannot happen</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (acc == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> hasNextService();</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                PrivilegedAction&lt;Boolean&gt; action = <span class="keyword">new</span> PrivilegedAction&lt;Boolean&gt;() &#123;</span><br><span class="line">                    <span class="function"><span class="keyword">public</span> Boolean <span class="title">run</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> hasNextService(); &#125;</span><br><span class="line">                &#125;;</span><br><span class="line">                <span class="keyword">return</span> AccessController.doPrivileged(action, acc);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> S <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (acc == <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> nextService();</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                PrivilegedAction&lt;S&gt; action = <span class="keyword">new</span> PrivilegedAction&lt;S&gt;() &#123;</span><br><span class="line">                    <span class="function"><span class="keyword">public</span> S <span class="title">run</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> nextService(); &#125;</span><br><span class="line">                &#125;;</span><br><span class="line">                <span class="keyword">return</span> AccessController.doPrivileged(action, acc);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Lazily loads the available providers of this loader&#x27;s service.</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span>  An iterator that lazily loads providers for this loader&#x27;s</span></span><br><span class="line"><span class="comment">     *          service</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Iterator&lt;S&gt; <span class="title">iterator</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> Iterator&lt;S&gt;() &#123;</span><br><span class="line"></span><br><span class="line">            Iterator&lt;Map.Entry&lt;String,S&gt;&gt; knownProviders</span><br><span class="line">                = providers.entrySet().iterator();</span><br><span class="line"></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">if</span> (knownProviders.hasNext())</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                <span class="keyword">return</span> lookupIterator.hasNext();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="function"><span class="keyword">public</span> S <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">if</span> (knownProviders.hasNext())</span><br><span class="line">                    <span class="keyword">return</span> knownProviders.next().getValue();</span><br><span class="line">                <span class="keyword">return</span> lookupIterator.next();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 重载的ServiceLoad */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;S&gt; <span class="function">ServiceLoader&lt;S&gt; <span class="title">load</span><span class="params">(Class&lt;S&gt; service,</span></span></span><br><span class="line"><span class="params"><span class="function">                                            ClassLoader loader)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> ServiceLoader&lt;&gt;(service, loader);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//DriverManage里面就是调用的这个方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;S&gt; <span class="function">ServiceLoader&lt;S&gt; <span class="title">load</span><span class="params">(Class&lt;S&gt; service)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//拿到了TCCL</span></span><br><span class="line">        ClassLoader cl = Thread.currentThread().getContextClassLoader();</span><br><span class="line">        <span class="comment">//调用上面重载的ServiceLoad方法</span></span><br><span class="line">        <span class="keyword">return</span> ServiceLoader.load(service, cl);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;S&gt; <span class="function">ServiceLoader&lt;S&gt; <span class="title">loadInstalled</span><span class="params">(Class&lt;S&gt; service)</span> </span>&#123;</span><br><span class="line">        ClassLoader cl = ClassLoader.getSystemClassLoader();</span><br><span class="line">        ClassLoader prev = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">while</span> (cl != <span class="keyword">null</span>) &#123;</span><br><span class="line">            prev = cl;</span><br><span class="line">            cl = cl.getParent();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ServiceLoader.load(service, prev);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;java.util.ServiceLoader[&quot;</span> + service.getName() + <span class="string">&quot;]&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>首先我们要明白一点，<code>Driver接口</code>，<code>DriverManage类</code>，以及<code>ServiceLoader</code>都是由<strong>根加载器</strong>去加载的(如果不相信的话可以用TraceClassLoading去查看)，所以在<code>ServiceLoader</code>中也是无法直接加载具体得实现类的</p>
</blockquote>
<p>前面<code>loadInitialDriver()</code>调用的就是这里的<code>ServiceLoader.load(Class&lt; S&gt; service)</code> 方法，这个方法中悄悄的拿到了<code>TCCL</code> ，而TCCL在<code>Launcher</code> 类（系统加载器和扩展加载器都是在Launcher中实现的）中默认设置成了系统加载器，具体可以去看一下源码这里我就不展开了，然后调用另一个重载的构造方法将<code>TCCL</code> 传递进去，最终调用了 <code>reload()</code>方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">reload</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    providers.clear();</span><br><span class="line">    <span class="comment">//初始化懒加载迭代器</span></span><br><span class="line">    lookupIterator = <span class="keyword">new</span> LazyIterator(service, loader);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p> 在这个方法中首先清空服务提供者(providers)缓存，然后初始化了一个<code>LazyIterator</code> 看名字就知道是啥意思了，其实到这里仍然没有任何具体的加载动作，因为这里采用的是按需加载，也就是懒加载，在迭代的时候才会去加载类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">LazyIterator</span> <span class="keyword">implements</span> <span class="title">Iterator</span>&lt;<span class="title">S</span>&gt;</span>&#123;</span><br><span class="line">	Class&lt;S&gt; service;</span><br><span class="line">    ClassLoader loader;</span><br><span class="line">    Enumeration&lt;URL&gt; configs = <span class="keyword">null</span>;</span><br><span class="line">    Iterator&lt;String&gt; pending = <span class="keyword">null</span>;</span><br><span class="line">    String nextName = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">LazyIterator</span><span class="params">(Class&lt;S&gt; service, ClassLoader loader)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.service = service;</span><br><span class="line">        <span class="keyword">this</span>.loader = loader;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">hasNextService</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (nextName != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (configs == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//拿到接口全名</span></span><br><span class="line">                String fullName = PREFIX + service.getName();</span><br><span class="line">                <span class="keyword">if</span> (loader == <span class="keyword">null</span>)</span><br><span class="line">                    configs = ClassLoader.getSystemResources(fullName);</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    configs = loader.getResources(fullName);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException x) &#123;</span><br><span class="line">                fail(service, <span class="string">&quot;Error locating configuration files&quot;</span>, x);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span> ((pending == <span class="keyword">null</span>) || !pending.hasNext()) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!configs.hasMoreElements()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//解析</span></span><br><span class="line">            pending = parse(service, configs.nextElement());</span><br><span class="line">        &#125;</span><br><span class="line">        nextName = pending.next();</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">	<span class="comment">//迭代器的next</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> S <span class="title">nextService</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!hasNextService())</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException();</span><br><span class="line">        String cn = nextName;</span><br><span class="line">        nextName = <span class="keyword">null</span>;</span><br><span class="line">        Class&lt;?&gt; c = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//具体加载类的地方就是在这里</span></span><br><span class="line">            <span class="comment">//利用前面传进来的TCCL加载实现类，但是不初始化类</span></span><br><span class="line">            c = Class.forName(cn, <span class="keyword">false</span>, loader);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ClassNotFoundException x) &#123;</span><br><span class="line">            fail(service,</span><br><span class="line">                 <span class="string">&quot;Provider &quot;</span> + cn + <span class="string">&quot; not found&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!service.isAssignableFrom(c)) &#123;</span><br><span class="line">            fail(service,</span><br><span class="line">                 <span class="string">&quot;Provider &quot;</span> + cn  + <span class="string">&quot; not a subtype&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//newInstance()实例化对应的实现类</span></span><br><span class="line">            S p = service.cast(c.newInstance());</span><br><span class="line">            <span class="comment">//put到providers中</span></span><br><span class="line">            providers.put(cn, p);</span><br><span class="line">            <span class="keyword">return</span> p;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable x) &#123;</span><br><span class="line">            fail(service,</span><br><span class="line">                 <span class="string">&quot;Provider &quot;</span> + cn + <span class="string">&quot; could not be instantiated&quot;</span>,</span><br><span class="line">                 x);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> Error();          <span class="comment">// This cannot happen</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (acc == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> hasNextService();</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            PrivilegedAction&lt;Boolean&gt; action = <span class="keyword">new</span> PrivilegedAction&lt;Boolean&gt;() &#123;</span><br><span class="line">                <span class="function"><span class="keyword">public</span> Boolean <span class="title">run</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> hasNextService(); &#125;</span><br><span class="line">            &#125;;</span><br><span class="line">            <span class="keyword">return</span> AccessController.doPrivileged(action, acc);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> S <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (acc == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> nextService();</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            PrivilegedAction&lt;S&gt; action = <span class="keyword">new</span> PrivilegedAction&lt;S&gt;() &#123;</span><br><span class="line">                <span class="function"><span class="keyword">public</span> S <span class="title">run</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> nextService(); &#125;</span><br><span class="line">            &#125;;</span><br><span class="line">            <span class="keyword">return</span> AccessController.doPrivileged(action, acc);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>🔸 可以看到在迭代器的<code>nextService()</code> 中才开始利用的<code>Class.forName()</code> 加载的具体实现类，而这个加载器正器正是前面<code>reload</code>传递过来的 <code>TCCL</code> 也就是默认的系统类加载器</p>
<p>🔸 随后在紧跟的try语句中通过 <code>newInstance()</code> 实例化了具体的实现类(MySql的驱动) ，然后put进providers队列并且返回实例化的实现类，但是在<code>loadInitialDrivers</code>中并没有接收这个返回，那他这里实例化是什么用意呢？</p>
<p>我们回到<code>getConnection()</code> 方法</p>
<h3 id="getConnection-源码"><a href="#getConnection-源码" class="headerlink" title="getConnection()源码"></a>getConnection()源码</h3><p>前面的静态方法调用完毕驱动已经加载，下面就是获取数据库连接了.</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Connection <span class="title">getConnection</span><span class="params">(String url,</span></span></span><br><span class="line"><span class="params"><span class="function">    String user, String password)</span> <span class="keyword">throws</span> SQLException </span>&#123;</span><br><span class="line">    java.util.Properties info = <span class="keyword">new</span> java.util.Properties();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (user != <span class="keyword">null</span>) &#123;</span><br><span class="line">        info.put(<span class="string">&quot;user&quot;</span>, user);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (password != <span class="keyword">null</span>) &#123;</span><br><span class="line">        info.put(<span class="string">&quot;password&quot;</span>, password);</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">// Reflection.getCallerClass()调用者的Class对象</span></span><br><span class="line">    <span class="keyword">return</span> (getConnection(url, info, Reflection.getCallerClass()));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>一般获取连接都是调用的上面这个方法，这个方法最终会调用另一个重载的方法，同时传入一个调用者的Class对象</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Connection <span class="title">getConnection</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function">    String url, java.util.Properties info, Class&lt;?&gt; caller)</span> <span class="keyword">throws</span> SQLException </span>&#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * When callerCl is null, we should check the application&#x27;s</span></span><br><span class="line"><span class="comment">     * (which is invoking this class indirectly)</span></span><br><span class="line"><span class="comment">     * classloader, so that the JDBC driver class outside rt.jar</span></span><br><span class="line"><span class="comment">     * can be loaded from here.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="comment">//Caller就是调用者的CLass也就是我们的应用代码类</span></span><br><span class="line">    <span class="comment">//获取到我们应用类的类加载器</span></span><br><span class="line">    ClassLoader callerCL = caller != <span class="keyword">null</span> ? caller.getClassLoader() : <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">synchronized</span>(DriverManager.class) &#123;</span><br><span class="line">        <span class="comment">// synchronize loading of the correct classloader.</span></span><br><span class="line">        <span class="keyword">if</span> (callerCL == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">//如果为空就，获取线程线下文加载器</span></span><br><span class="line">            callerCL = Thread.currentThread().getContextClassLoader();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(url == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> SQLException(<span class="string">&quot;The url cannot be null&quot;</span>, <span class="string">&quot;08001&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    println(<span class="string">&quot;DriverManager.getConnection(\&quot;&quot;</span> + url + <span class="string">&quot;\&quot;)&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Walk through the loaded registeredDrivers attempting to make a connection.</span></span><br><span class="line">    <span class="comment">// Remember the first exception that gets raised so we can reraise it.</span></span><br><span class="line">    SQLException reason = <span class="keyword">null</span>;</span><br><span class="line">	</span><br><span class="line">    <span class="comment">//遍历这个registeredDrivers里面都是DriverInfo</span></span><br><span class="line">    <span class="keyword">for</span>(DriverInfo aDriver : registeredDrivers) &#123;</span><br><span class="line">        <span class="comment">// If the caller does not have permission to load the driver then</span></span><br><span class="line">        <span class="comment">// skip it.</span></span><br><span class="line">        <span class="comment">//检查加载驱动的加载器是不是调用者的类加载器</span></span><br><span class="line">        <span class="keyword">if</span>(isDriverAllowed(aDriver.driver, callerCL)) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                println(<span class="string">&quot;    trying &quot;</span> + aDriver.driver.getClass().getName());</span><br><span class="line">                <span class="comment">//获取连接</span></span><br><span class="line">                Connection con = aDriver.driver.connect(url, info);</span><br><span class="line">                <span class="keyword">if</span> (con != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="comment">// Success!</span></span><br><span class="line">                    println(<span class="string">&quot;getConnection returning &quot;</span> + aDriver.driver.getClass().getName());</span><br><span class="line">                    <span class="keyword">return</span> (con);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (SQLException ex) &#123;</span><br><span class="line">                <span class="keyword">if</span> (reason == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    reason = ex;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            println(<span class="string">&quot;    skipping: &quot;</span> + aDriver.getClass().getName());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// if we got here nobody could connect.</span></span><br><span class="line">    <span class="keyword">if</span> (reason != <span class="keyword">null</span>)    &#123;</span><br><span class="line">        println(<span class="string">&quot;getConnection failed: &quot;</span> + reason);</span><br><span class="line">        <span class="keyword">throw</span> reason;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    println(<span class="string">&quot;getConnection: no suitable driver found for &quot;</span>+ url);</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> SQLException(<span class="string">&quot;No suitable driver found for &quot;</span>+ url, <span class="string">&quot;08001&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>🔸 可以看到中间有一个foreach循环，遍历<code>registeredDrivers</code>，这是个<code>CopyOnWriteArrayList</code> 这个类看名字就知道存放的是已经注册的<code>Drivers</code> 实现类，那这些实现类是什么时候注册进来的呢？回到我们之前抛出的一个问题，在<code>ServiceLoader</code>的迭代器中加载了具体的类之后进行了实例化，但是<code>DriverManager</code> 中并没有接收这个实例，我们来看一下具体的驱动实现类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> com.mysql.cj.jdbc;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.sql.DriverManager;</span><br><span class="line"><span class="keyword">import</span> java.sql.SQLException;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Driver</span> <span class="keyword">extends</span> <span class="title">NonRegisteringDriver</span> <span class="keyword">implements</span> <span class="title">java</span>.<span class="title">sql</span>.<span class="title">Driver</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Driver</span><span class="params">()</span> <span class="keyword">throws</span> SQLException </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">//注册到DriverManager中去</span></span><br><span class="line">            DriverManager.registerDriver(<span class="keyword">new</span> Driver());</span><br><span class="line">        &#125; <span class="keyword">catch</span> (SQLException var1) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">&quot;Can&#x27;t register driver!&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>🔸 相信看到这里大家就明白了，前面初始化这个类的作用就是为了能触发这个<strong>静态代码块</strong>，进而执行registerDriver()方法进行注册，真是妙啊👏一环套一环</p>
<p>🔸 还有一个需要注意的地方就是这里注册的虽然是个空的Driver类，但是别忘了它还有个父类NonRegisteringDriver</p>
<p>这个类才是真正的实现类具体的<code>connect()</code> 等方法都是在这个里面实现的，而Driver继承了它的方法</p>
<p>🔸 在遍历<code>registeredDrivers</code> 的时候还调用了一个<code>isDriverAllowed(aDriver.driver, callerCL)</code> 方法这个方法第一个参数就是驱动的实现类，第二个参数就是前面获取到的<strong>调用者的类加载器</strong> ，作用就是通过利用传进来的加载器尝试加载这个类，然后判断是不是同一个类，（众所周知不同的加载器因为命名空间的存在，即使加载同一份字节码文件得到的也不是一个类） 如果是就允许加载，否则不允许，为啥要这样做呢？其实还是因为命名空间的问题，因为有了SPI的机制，你<strong>加载初始化这个实现类的加载器</strong>(TCCL)和最终去<strong>调用实现类的方法的类的加载器</strong>有可能不是同一个，因为程序员可以很容易的将TCCL修改成其他的类加载器，如果不保证一致的话后面就会出现<code>ClassCastException</code>等异常 </p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">isDriverAllowed</span><span class="params">(Driver driver, ClassLoader classLoader)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">boolean</span> result = <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">if</span>(driver != <span class="keyword">null</span>) &#123;</span><br><span class="line">        Class&lt;?&gt; aClass = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            aClass =  Class.forName(driver.getClass().getName(), <span class="keyword">true</span>, classLoader);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception ex) &#123;</span><br><span class="line">            result = <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">         result = ( aClass == driver.getClass() ) ? <span class="keyword">true</span> : <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a><strong>总结</strong></h3><p>当高层提供了统一接口让低层去实现（面向接口编程，解耦），同时又要是在高层加载（或实例化）低层的类时，必须通过线程上下文类加载器来帮助高层的ClassLoader找到并加载该类。</p>
<h2 id="Jar-Hell"><a href="#Jar-Hell" class="headerlink" title="Jar Hell"></a>Jar Hell</h2><p>Jar包地狱，<a class="link"   target="_blank" rel="noopener" href="https://www.hidennis.tech/2016/05/30/what-is-jar-hell/" >参考<i class="fas fa-external-link-alt"></i></a></p>
<p>这个问题其实可以通过<code>OSGI</code>等组件化框架来解决，使用OSGI可以完美解决这个问题，OSGI是基于模块（Bundle）驱动的，每个模块都有属于自己的classpath和类加载器，模块之间通过包暴露和引入进行关联，每个模块有着自己独立的生命周期，我们可以动态地对模块进行加载、卸载、更新。如此看来，OSGI可以用一句话描述，就是一个为Java提供的动态模块化的系统。但是OSGI太过复杂，实用性并不强</p>
<p><a class="link"   target="_blank" rel="noopener" href="http://hellojava.info/?p=152" >阿里架构师对OSGI的评价<i class="fas fa-external-link-alt"></i></a></p>
<p>这里我主要想说的是怎么在代码中利用类加载器来检测Jar Hell</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JarHell</span> </span>&#123;  </span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;  </span><br><span class="line">        <span class="keyword">try</span> &#123;  </span><br><span class="line">            Enumeration&lt;URL&gt; urls = Thread.currentThread().getContextClassLoader().getResources(<span class="string">&quot;org/apache/log4j/Logger.class&quot;</span>);  </span><br><span class="line">            <span class="keyword">while</span>(urls.hasMoreElements()) &#123;  </span><br><span class="line">                URL url = urls.nextElement();  </span><br><span class="line">                System.out.println(url);  </span><br><span class="line">            &#125;  </span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;  </span><br><span class="line">            e.printStackTrace();  </span><br><span class="line">        &#125;  </span><br><span class="line">       </span><br><span class="line">    &#125;  </span><br><span class="line">&#125;  </span><br></pre></td></tr></table></figure>

<p>这样就可以找到classpath中冲突的jar包，当然通过idea的工具会更方便 😂</p>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p>《深入理解Java虚拟机》</p>
<p><a class="link"   target="_blank" rel="noopener" href="https://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html#code4" >深入探讨 Java 类加载器<i class="fas fa-external-link-alt"></i></a></p>

        </div>

        
            <div class="post-copyright-info">
                <div class="article-copyright-info-container">
    <ul>
        <li>本文标题：深入理解Java虚拟机（二）</li>
        <li>本文作者：Resolmi</li>
        <li>创建时间：2019-08-17 00:00:00</li>
        <li>
            本文链接：https://imlgw.top/2019/08/17/2c0986bb/
        </li>
        <li>
            版权声明：本博客所有文章除特别声明外，均采用 <a class="license" target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">BY-NC-SA</a> 许可协议。转载请注明出处！
        </li>
    </ul>
</div>

            </div>
        

        
            <div class="article-nav">
                
                    <div class="article-prev">
                        <a class="prev"
                           rel="prev"
                           href="/2019/09/01/ece53aaa/"
                        >
                            <span class="left arrow-icon flex-center">
                              <i class="fas fa-chevron-left"></i>
                            </span>
                            <span class="title flex-center">
                                <span class="post-nav-title-item">LeetCode动态规划</span>
                                <span class="post-nav-item">上一篇</span>
                            </span>
                        </a>
                    </div>
                
                
                    <div class="article-next">
                        <a class="next"
                           rel="next"
                           href="/2019/08/11/1c1f6a63/"
                        >
                            <span class="title flex-center">
                                <span class="post-nav-title-item">深入理解Java虚拟机（一）</span>
                                <span class="post-nav-item">下一篇</span>
                            </span>
                            <span class="right arrow-icon flex-center">
                              <i class="fas fa-chevron-right"></i>
                            </span>
                        </a>
                    </div>
                
            </div>
        

        
            <div class="comment-container">
                <div class="comments-container">
    <div id="comment-anchor"></div>
    <div class="comment-area-title">
        <i class="fas fa-comments">&nbsp;评论</i>
    </div>
    

        
            <section class="disqus-comments">
<div id="disqus_thread">
  <noscript>Please enable JavaScript to view the <a target="_blank" rel="noopener" href="//disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</div>
</section>

<script>
var disqus_shortname = 'imlgw';

var disqus_url = 'https://imlgw.top/2019/08/17/2c0986bb/';

(function(){
  var dsq = document.createElement('script');
  dsq.type = 'text/javascript';
  dsq.async = true;
  dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
  (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>

<script id="dsq-count-scr" src="//imlgw.disqus.com/count.js" async></script>
        
    
</div>

            </div>
        
    </div>
</div>


                
            </div>

        </div>

        <div class="page-main-content-bottom">
            <footer class="footer">
    <div class="info-container">
        <div class="copyright-info info-item">
            &copy;
            
              <span>2018</span>&nbsp;-&nbsp;
            
            2021&nbsp;<i class="fas fa-heart icon-animate"></i>&nbsp;<a href="/">Resolmi</a>
        </div>
        
            <script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
            <div class="website-count info-item">
                
                    <span id="busuanzi_container_site_uv">
                        访问人数&nbsp;<span id="busuanzi_value_site_uv"></span>&ensp;
                    </span>
                
                
                    <span id="busuanzi_container_site_pv">
                        总访问量&nbsp;<span id="busuanzi_value_site_pv"></span>
                    </span>
                
            </div>
        
        
            <div class="icp-info info-item"><a target="_blank" rel="nofollow" href="https://beian.miit.gov.cn">鄂ICP备18011208号</a></div>
        
    </div>
</footer>

        </div>
    </div>

    
        <div class="post-tools">
            <div class="post-tools-container">
    <ul class="tools-list">
        <!-- TOC aside toggle -->
        
            <li class="tools-item page-aside-toggle">
                <i class="fas fa-outdent"></i>
            </li>
        

        <!-- go comment -->
        
            <li class="go-comment">
                <i class="fas fa-comment"></i>
            </li>
        
    </ul>
</div>

        </div>
    

    <div class="right-bottom-side-tools">
        <div class="side-tools-container">
    <ul class="side-tools-list">
        <li class="tools-item tool-font-adjust-plus flex-center">
            <i class="fas fa-search-plus"></i>
        </li>

        <li class="tools-item tool-font-adjust-minus flex-center">
            <i class="fas fa-search-minus"></i>
        </li>

        <li class="tools-item tool-expand-width flex-center">
            <i class="fas fa-arrows-alt-h"></i>
        </li>

        <li class="tools-item tool-dark-light-toggle flex-center">
            <i class="fas fa-moon"></i>
        </li>

        <!-- rss -->
        

        

        <li class="tools-item tool-scroll-to-bottom flex-center">
            <i class="fas fa-arrow-down"></i>
        </li>
    </ul>

    <ul class="exposed-tools-list">
        <li class="tools-item tool-toggle-show flex-center">
            <i class="fas fa-cog fa-spin"></i>
        </li>
        
            <li class="tools-item tool-scroll-to-top flex-center">
                <i class="arrow-up fas fa-arrow-up"></i>
                <span class="percent"></span>
            </li>
        
    </ul>
</div>

    </div>

    
        <aside class="page-aside">
            <div class="post-toc-wrap">
    <div class="post-toc">
        <ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E7%9A%84%E8%BF%87%E7%A8%8B"><span class="nav-number">1.</span> <span class="nav-text">类加载的过程</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E2%91%A0%E5%8A%A0%E8%BD%BD"><span class="nav-number">1.1.</span> <span class="nav-text">①加载</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E7%9A%84%E6%9C%80%E7%BB%88%E4%BA%A7%E7%89%A9"><span class="nav-number">1.1.1.</span> <span class="nav-text">类加载的最终产物</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%8A%A0%E8%BD%BD%E7%B1%BB%E7%9A%84%E6%96%B9%E5%BC%8F"><span class="nav-number">1.1.2.</span> <span class="nav-text">加载类的方式</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E2%91%A1%E8%BF%9E%E6%8E%A5"><span class="nav-number">1.2.</span> <span class="nav-text">②连接:</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E9%AA%8C%E8%AF%81"><span class="nav-number">1.2.1.</span> <span class="nav-text">验证</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%87%86%E5%A4%87"><span class="nav-number">1.2.2.</span> <span class="nav-text">准备</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E8%A7%A3%E6%9E%90"><span class="nav-number">1.2.3.</span> <span class="nav-text">解析</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E2%91%A2%E5%88%9D%E5%A7%8B%E5%8C%96%EF%BC%9A"><span class="nav-number">1.3.</span> <span class="nav-text">③初始化：</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E7%B1%BB%E6%9E%84%E9%80%A0%E5%99%A8-lt-client-gt"><span class="nav-number">1.3.1.</span> <span class="nav-text">类构造器&lt;client&gt;</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E4%B8%BA%E7%B1%BB%E7%9A%84%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F%E8%B5%8B%E4%BA%88%E6%AD%A3%E7%A1%AE%E7%9A%84%E5%88%9D%E5%A7%8B%E5%80%BC"><span class="nav-number">1.3.2.</span> <span class="nav-text">为类的静态变量赋予正确的初始值</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Class%E5%AF%B9%E8%B1%A1%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F"><span class="nav-number">1.4.</span> <span class="nav-text">Class对象在哪里？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Java%E7%A8%8B%E5%BA%8F%E5%AF%B9%E7%B1%BB%E7%9A%84%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F"><span class="nav-number">2.</span> <span class="nav-text">Java程序对类的使用方式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E4%B8%BB%E5%8A%A8%E4%BD%BF%E7%94%A8"><span class="nav-number">2.1.</span> <span class="nav-text">主动使用</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%A2%AB%E5%8A%A8%E4%BD%BF%E7%94%A8"><span class="nav-number">2.2.</span> <span class="nav-text">被动使用</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%AF%B9%E8%B1%A1%E7%9A%84%E8%AE%BF%E9%97%AE%E5%AE%9A%E4%BD%8D"><span class="nav-number">2.3.</span> <span class="nav-text">对象的访问定位</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#JVM%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="nav-number">3.</span> <span class="nav-text">JVM类加载器</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%A6%82%E8%BF%B0"><span class="nav-number">3.1.</span> <span class="nav-text">概述</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="nav-number">3.2.</span> <span class="nav-text">自定义类加载器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%BC%8F"><span class="nav-number">3.3.</span> <span class="nav-text">双亲委派模式</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8A%A0%E5%AF%86%E8%A7%A3%E5%AF%86%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8"><span class="nav-number">3.4.</span> <span class="nav-text">加密解密类加载器</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%89%93%E7%A0%B4%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%9C%BA%E5%88%B6"><span class="nav-number">3.5.</span> <span class="nav-text">打破双亲委派机制</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8%E7%BB%86%E8%8A%82%E6%B7%B1%E5%85%A5"><span class="nav-number">3.6.</span> <span class="nav-text">类加载器细节深入</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%BA%BF%E7%A8%8B%E4%B8%8A%E4%B8%8B%E6%96%87%E5%8A%A0%E8%BD%BD%E5%99%A8-TCCL"><span class="nav-number">4.</span> <span class="nav-text">线程上下文加载器(TCCL)</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#JDBC%E6%BA%90%E7%A0%81%E6%A1%88%E4%BE%8B%E5%88%86%E6%9E%90"><span class="nav-number">4.1.</span> <span class="nav-text">JDBC源码案例分析</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#loadInitialDrivers-%E6%BA%90%E7%A0%81"><span class="nav-number">4.2.</span> <span class="nav-text">loadInitialDrivers()源码</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#ServiceLoader%E7%B1%BB%E6%BA%90%E7%A0%81"><span class="nav-number">4.3.</span> <span class="nav-text">ServiceLoader类源码</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#getConnection-%E6%BA%90%E7%A0%81"><span class="nav-number">4.4.</span> <span class="nav-text">getConnection()源码</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%80%BB%E7%BB%93"><span class="nav-number">4.5.</span> <span class="nav-text">总结</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Jar-Hell"><span class="nav-number">5.</span> <span class="nav-text">Jar Hell</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8F%82%E8%80%83"><span class="nav-number">6.</span> <span class="nav-text">参考</span></a></li></ol>
    </div>
</div>
        </aside>
    

    <div class="image-viewer-container">
    <img src="">
</div>


    
        <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
          <span class="search-input-field-pre">
            <i class="fas fa-keyboard"></i>
          </span>
            <div class="search-input-container">
                <input autocomplete="off"
                       autocorrect="off"
                       autocapitalize="off"
                       placeholder="搜索..."
                       spellcheck="false"
                       type="search"
                       class="search-input"
                >
            </div>
            <span class="popup-btn-close">
                <i class="fas fa-times"></i>
            </span>
        </div>
        <div id="search-result">
            <div id="no-result">
                <i class="fas fa-spinner fa-pulse fa-5x fa-fw"></i>
            </div>
        </div>
    </div>
</div>

    

</main>



<script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/utils.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/main.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/header-shrink.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/back2top.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/dark-light-toggle.js"></script>


    <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/local-search.js"></script>



    <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/code-copy.js"></script>



    <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/lazyload.js"></script>


<div class="post-scripts pjax">
    
        <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/left-side-toggle.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/libs/anime.min.js"></script><script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/toc.js"></script>
    
</div>


    <script src="//cdn.jsdelivr.net/npm/hexo-theme-keep@3.4.3/source/js/libs/pjax.min.js"></script>
<script>
    window.addEventListener('DOMContentLoaded', () => {
        window.pjax = new Pjax({
            selectors: [
                'head title',
                '.page-container',
                '.pjax'
            ],
            history: true,
            debug: false,
            cacheBust: false,
            timeout: 0,
            analytics: false,
            currentUrlFullReload: false,
            scrollRestoration: false,
            // scrollTo: true,
        });

        document.addEventListener('pjax:send', () => {
            KEEP.utils.pjaxProgressBarStart();
        });

        document.addEventListener('pjax:complete', () => {
            KEEP.utils.pjaxProgressBarEnd();
            window.pjax.executeScripts(document.querySelectorAll('script[data-pjax], .pjax script'));
            KEEP.refresh();
        });
    });
</script>



<script src="https://cdn.jsdelivr.net/npm/live2d-widget@3.x/lib/L2Dwidget.min.js"></script><script>L2Dwidget.init({"pluginRootPath":"live2dw/","pluginJsPath":"lib/","pluginModelPath":"assets/","tagMode":false,"debug":false,"model":{"jsonPath":"https://cdn.jsdelivr.net/npm/live2d-widget-model-hijiki@1.0.5/assets/hijiki.model.json"},"display":{"superSample":2,"width":160,"height":320,"position":"right","hOffset":0,"vOffset":-70},"mobile":{"show":false,"scale":0.2},"log":false});</script></body>
</html>
